linkgress-orm 0.4.59 → 0.4.61

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 (38) hide show
  1. package/dist/entity/entity-base.d.ts +16 -0
  2. package/dist/entity/entity-base.d.ts.map +1 -1
  3. package/dist/entity/entity-base.js +1 -0
  4. package/dist/entity/entity-base.js.map +1 -1
  5. package/dist/entity/entity-builder.d.ts +23 -0
  6. package/dist/entity/entity-builder.d.ts.map +1 -1
  7. package/dist/entity/entity-builder.js +34 -0
  8. package/dist/entity/entity-builder.js.map +1 -1
  9. package/dist/entity/model-config.d.ts.map +1 -1
  10. package/dist/entity/model-config.js +4 -0
  11. package/dist/entity/model-config.js.map +1 -1
  12. package/dist/migration/check-constraint-sql.d.ts +45 -0
  13. package/dist/migration/check-constraint-sql.d.ts.map +1 -0
  14. package/dist/migration/check-constraint-sql.js +41 -0
  15. package/dist/migration/check-constraint-sql.js.map +1 -0
  16. package/dist/migration/db-schema-manager.d.ts +27 -0
  17. package/dist/migration/db-schema-manager.d.ts.map +1 -1
  18. package/dist/migration/db-schema-manager.js +213 -117
  19. package/dist/migration/db-schema-manager.js.map +1 -1
  20. package/dist/migration/migration-scaffold.d.ts.map +1 -1
  21. package/dist/migration/migration-scaffold.js +19 -0
  22. package/dist/migration/migration-scaffold.js.map +1 -1
  23. package/dist/query/conditions.d.ts +8 -0
  24. package/dist/query/conditions.d.ts.map +1 -1
  25. package/dist/query/conditions.js.map +1 -1
  26. package/dist/query/query-builder.d.ts +14 -0
  27. package/dist/query/query-builder.d.ts.map +1 -1
  28. package/dist/query/query-builder.js +53 -3
  29. package/dist/query/query-builder.js.map +1 -1
  30. package/dist/query/union-builder.d.ts +7 -0
  31. package/dist/query/union-builder.d.ts.map +1 -1
  32. package/dist/query/union-builder.js +66 -0
  33. package/dist/query/union-builder.js.map +1 -1
  34. package/dist/schema/table-builder.d.ts +23 -0
  35. package/dist/schema/table-builder.d.ts.map +1 -1
  36. package/dist/schema/table-builder.js +10 -0
  37. package/dist/schema/table-builder.js.map +1 -1
  38. package/package.json +1 -1
@@ -40,6 +40,7 @@ const collation_builder_1 = require("../types/collation-builder");
40
40
  const conditions_1 = require("../query/conditions");
41
41
  const index_sql_1 = require("./index-sql");
42
42
  const statistics_sql_1 = require("./statistics-sql");
43
+ const check_constraint_sql_1 = require("./check-constraint-sql");
43
44
  const dbsetting_sql_1 = require("./dbsetting-sql");
44
45
  const partition_sql_1 = require("./partition-sql");
45
46
  /**
@@ -128,10 +129,10 @@ class DbSchemaManager {
128
129
  }
129
130
  for (const [enumName, enumDef] of enums.entries()) {
130
131
  // Check if enum already exists
131
- const checkSQL = `
132
- SELECT EXISTS (
133
- SELECT 1 FROM pg_type WHERE typname = $1
134
- ) as exists
132
+ const checkSQL = `
133
+ SELECT EXISTS (
134
+ SELECT 1 FROM pg_type WHERE typname = $1
135
+ ) as exists
135
136
  `;
136
137
  const result = await this.client.query(checkSQL, [enumName]);
137
138
  const exists = result.rows[0]?.exists;
@@ -162,10 +163,10 @@ class DbSchemaManager {
162
163
  this.logger('Creating collations...\n');
163
164
  }
164
165
  for (const [collationName, collationDef] of collations.entries()) {
165
- const checkSQL = `
166
- SELECT EXISTS (
167
- SELECT 1 FROM pg_collation WHERE collname = $1
168
- ) as exists
166
+ const checkSQL = `
167
+ SELECT EXISTS (
168
+ SELECT 1 FROM pg_collation WHERE collname = $1
169
+ ) as exists
169
170
  `;
170
171
  const result = await this.client.query(checkSQL, [collationName]);
171
172
  const exists = result.rows[0]?.exists;
@@ -200,13 +201,13 @@ class DbSchemaManager {
200
201
  : `"${config.name}"`;
201
202
  // Check if sequence already exists
202
203
  const checkSQL = config.schema
203
- ? `SELECT EXISTS (
204
- SELECT 1 FROM information_schema.sequences
205
- WHERE sequence_schema = $1 AND sequence_name = $2
204
+ ? `SELECT EXISTS (
205
+ SELECT 1 FROM information_schema.sequences
206
+ WHERE sequence_schema = $1 AND sequence_name = $2
206
207
  ) as exists`
207
- : `SELECT EXISTS (
208
- SELECT 1 FROM information_schema.sequences
209
- WHERE sequence_name = $1 AND sequence_schema = 'public'
208
+ : `SELECT EXISTS (
209
+ SELECT 1 FROM information_schema.sequences
210
+ WHERE sequence_name = $1 AND sequence_schema = 'public'
210
211
  ) as exists`;
211
212
  const checkParams = config.schema ? [config.schema, config.name] : [config.name];
212
213
  const result = await this.client.query(checkSQL, checkParams);
@@ -347,10 +348,10 @@ class DbSchemaManager {
347
348
  (0, partition_sql_1.validatePartitioningPrimaryKey)(tableSchema.partitioning, pkColumnNames, tableName);
348
349
  partitionByClause = ` ${(0, partition_sql_1.buildPartitionByClause)(tableSchema.partitioning)}`;
349
350
  }
350
- const createTableSQL = `
351
- CREATE TABLE IF NOT EXISTS ${qualifiedTableName} (
352
- ${columnDefs.join(',\n ')}
353
- )${partitionByClause}
351
+ const createTableSQL = `
352
+ CREATE TABLE IF NOT EXISTS ${qualifiedTableName} (
353
+ ${columnDefs.join(',\n ')}
354
+ )${partitionByClause}
354
355
  `;
355
356
  if (this.logQueries) {
356
357
  this.logger(` Creating table ${qualifiedTableName}...`);
@@ -568,6 +569,11 @@ class DbSchemaManager {
568
569
  for (const [tableName, tableSchema] of sortedTables) {
569
570
  await this.createIndexes(tableName, tableSchema);
570
571
  }
572
+ // Create CHECK constraints (existence-guarded — no ADD CONSTRAINT IF NOT
573
+ // EXISTS in PostgreSQL, and ensureCreated must stay idempotent)
574
+ for (const [tableName, tableSchema] of sortedTables) {
575
+ await this.createCheckConstraints(tableName, tableSchema);
576
+ }
571
577
  // Create extended-statistics objects (after indexes so the ANALYZE they
572
578
  // trigger also refreshes freshly indexed expression columns)
573
579
  for (const [tableName, tableSchema] of sortedTables) {
@@ -608,6 +614,23 @@ class DbSchemaManager {
608
614
  await this.executeCreateStatistics(tableName, spec, tableSchema.schema);
609
615
  }
610
616
  }
617
+ /**
618
+ * Create the declared CHECK constraints of a table that are missing on it.
619
+ * Reconciled by NAME (see check-constraint-sql.ts) — existing same-named
620
+ * constraints are left untouched, so the pass is idempotent.
621
+ */
622
+ async createCheckConstraints(tableName, tableSchema) {
623
+ const checkConstraints = tableSchema.checkConstraints || [];
624
+ if (checkConstraints.length === 0) {
625
+ return;
626
+ }
627
+ const existing = await this.getExistingCheckConstraints(tableName, tableSchema.schema);
628
+ for (const spec of checkConstraints) {
629
+ if (!existing.includes(spec.name)) {
630
+ await this.executeCreateCheckConstraint(tableName, spec, tableSchema.schema);
631
+ }
632
+ }
633
+ }
611
634
  /**
612
635
  * Converge declared database-level settings (`model.hasDbSetting`) — apply
613
636
  * every declaration that is missing from `pg_db_role_setting` or whose
@@ -640,11 +663,11 @@ class DbSchemaManager {
640
663
  * keyed by lower-cased parameter name (GUC names are case-insensitive).
641
664
  */
642
665
  async getExistingDatabaseSettings() {
643
- const result = await this.client.query(`
644
- SELECT entry
645
- FROM pg_db_role_setting, LATERAL unnest(setconfig) AS entry
646
- WHERE setrole = 0
647
- AND setdatabase = (SELECT oid FROM pg_database WHERE datname = current_database())
666
+ const result = await this.client.query(`
667
+ SELECT entry
668
+ FROM pg_db_role_setting, LATERAL unnest(setconfig) AS entry
669
+ WHERE setrole = 0
670
+ AND setdatabase = (SELECT oid FROM pg_database WHERE datname = current_database())
648
671
  `);
649
672
  const settings = new Map();
650
673
  for (const row of result.rows) {
@@ -750,10 +773,10 @@ class DbSchemaManager {
750
773
  }
751
774
  // Add schema creation operations if needed
752
775
  for (const schemaName of modelSchemas) {
753
- const result = await this.client.query(`
754
- SELECT EXISTS (
755
- SELECT 1 FROM information_schema.schemata WHERE schema_name = $1
756
- ) as exists
776
+ const result = await this.client.query(`
777
+ SELECT EXISTS (
778
+ SELECT 1 FROM information_schema.schemata WHERE schema_name = $1
779
+ ) as exists
757
780
  `, [schemaName]);
758
781
  if (!result.rows[0]?.exists) {
759
782
  operations.push({ type: 'create_schema', schemaName });
@@ -762,10 +785,10 @@ class DbSchemaManager {
762
785
  // Check collations
763
786
  const modelCollations = collation_builder_1.CollationRegistry.getAll();
764
787
  for (const [collationName, collationDef] of modelCollations.entries()) {
765
- const result = await this.client.query(`
766
- SELECT EXISTS (
767
- SELECT 1 FROM pg_collation WHERE collname = $1
768
- ) as exists
788
+ const result = await this.client.query(`
789
+ SELECT EXISTS (
790
+ SELECT 1 FROM pg_collation WHERE collname = $1
791
+ ) as exists
769
792
  `, [collationName]);
770
793
  if (!result.rows[0]?.exists) {
771
794
  operations.push({ type: 'create_collation', collation: collationDef });
@@ -774,11 +797,11 @@ class DbSchemaManager {
774
797
  // Check enums
775
798
  const modelEnums = enum_builder_1.EnumTypeRegistry.getAll();
776
799
  for (const [enumName, enumDef] of modelEnums.entries()) {
777
- const existing = await this.client.query(`
778
- SELECT e.enumlabel
779
- FROM pg_type t
780
- JOIN pg_enum e ON e.enumtypid = t.oid
781
- WHERE t.typname = $1
800
+ const existing = await this.client.query(`
801
+ SELECT e.enumlabel
802
+ FROM pg_type t
803
+ JOIN pg_enum e ON e.enumtypid = t.oid
804
+ WHERE t.typname = $1
782
805
  `, [enumName]);
783
806
  if (existing.rows.length === 0) {
784
807
  // Enum type doesn't exist yet — create it with the full value set.
@@ -799,13 +822,13 @@ class DbSchemaManager {
799
822
  // Check sequences
800
823
  for (const [_, config] of this.sequenceRegistry.entries()) {
801
824
  const checkSQL = config.schema
802
- ? `SELECT EXISTS (
803
- SELECT 1 FROM information_schema.sequences
804
- WHERE sequence_schema = $1 AND sequence_name = $2
825
+ ? `SELECT EXISTS (
826
+ SELECT 1 FROM information_schema.sequences
827
+ WHERE sequence_schema = $1 AND sequence_name = $2
805
828
  ) as exists`
806
- : `SELECT EXISTS (
807
- SELECT 1 FROM information_schema.sequences
808
- WHERE sequence_name = $1 AND sequence_schema = 'public'
829
+ : `SELECT EXISTS (
830
+ SELECT 1 FROM information_schema.sequences
831
+ WHERE sequence_name = $1 AND sequence_schema = 'public'
809
832
  ) as exists`;
810
833
  const checkParams = config.schema ? [config.schema, config.name] : [config.name];
811
834
  const result = await this.client.query(checkSQL, checkParams);
@@ -921,6 +944,22 @@ class DbSchemaManager {
921
944
  });
922
945
  }
923
946
  }
947
+ // Compare CHECK constraints — by NAME only (create missing; never
948
+ // drop or rebuild — rename a constraint to change its definition).
949
+ if ((schema.checkConstraints || []).length > 0) {
950
+ const existingCheckConstraints = await this.getExistingCheckConstraints(tableName, schema.schema);
951
+ for (const modelCheck of schema.checkConstraints || []) {
952
+ if (!existingCheckConstraints.includes(modelCheck.name)) {
953
+ operations.push({
954
+ type: 'create_check_constraint',
955
+ tableName,
956
+ schema: schema.schema,
957
+ constraintName: modelCheck.name,
958
+ expression: modelCheck.expression,
959
+ });
960
+ }
961
+ }
962
+ }
924
963
  // Compare foreign key constraints
925
964
  const existingForeignKeys = await this.getExistingForeignKeys(tableName, schema.schema);
926
965
  const modelForeignKeys = schema.foreignKeys || [];
@@ -1046,6 +1085,16 @@ class DbSchemaManager {
1046
1085
  nullsNotDistinct: index.nullsNotDistinct,
1047
1086
  });
1048
1087
  }
1088
+ // Add CHECK constraints for newly created tables to phase 3
1089
+ for (const check of schema.checkConstraints || []) {
1090
+ phase3Ops.push({
1091
+ type: 'create_check_constraint',
1092
+ tableName,
1093
+ schema: schema.schema,
1094
+ constraintName: check.name,
1095
+ expression: check.expression,
1096
+ });
1097
+ }
1049
1098
  }
1050
1099
  }
1051
1100
  const totalOps = phase1Ops.length + phase2Ops.length + phase3Ops.length;
@@ -1169,6 +1218,12 @@ class DbSchemaManager {
1169
1218
  kinds: operation.kinds,
1170
1219
  }, operation.schema);
1171
1220
  break;
1221
+ case 'create_check_constraint':
1222
+ await this.executeCreateCheckConstraint(operation.tableName, {
1223
+ name: operation.constraintName,
1224
+ expression: operation.expression,
1225
+ }, operation.schema);
1226
+ break;
1172
1227
  case 'set_database_setting':
1173
1228
  await this.executeSetDatabaseSetting(operation);
1174
1229
  break;
@@ -1447,16 +1502,55 @@ class DbSchemaManager {
1447
1502
  * the table's namespace so the lookup is independent of search_path.
1448
1503
  */
1449
1504
  async getExistingStatistics(tableName, schemaName) {
1450
- const result = await this.client.query(`
1451
- SELECT s.stxname
1452
- FROM pg_statistic_ext s
1453
- JOIN pg_class c ON c.oid = s.stxrelid
1454
- JOIN pg_namespace n ON n.oid = c.relnamespace
1455
- WHERE n.nspname = $1
1456
- AND c.relname = $2
1505
+ const result = await this.client.query(`
1506
+ SELECT s.stxname
1507
+ FROM pg_statistic_ext s
1508
+ JOIN pg_class c ON c.oid = s.stxrelid
1509
+ JOIN pg_namespace n ON n.oid = c.relnamespace
1510
+ WHERE n.nspname = $1
1511
+ AND c.relname = $2
1457
1512
  `, [schemaName || 'public', tableName]);
1458
1513
  return result.rows.map((row) => row.stxname);
1459
1514
  }
1515
+ /**
1516
+ * Add a CHECK constraint, guarded by a fresh existence lookup (PostgreSQL
1517
+ * has no ADD CONSTRAINT IF NOT EXISTS) so replays stay idempotent.
1518
+ * PostgreSQL validates existing rows as part of the ALTER — a table holding
1519
+ * violating rows fails the statement loudly; backfill data first.
1520
+ */
1521
+ async executeCreateCheckConstraint(tableName, spec, schema) {
1522
+ if (!spec.expression || spec.expression.trim().length === 0) {
1523
+ throw new Error(`Check constraint "${spec.name}" has no expression. Pass the raw SQL boolean expression as the second argument of hasCheckConstraint().`);
1524
+ }
1525
+ const existing = await this.getExistingCheckConstraints(tableName, schema);
1526
+ if (existing.includes(spec.name)) {
1527
+ return;
1528
+ }
1529
+ const qualifiedTableName = this.getQualifiedTableName(tableName, schema);
1530
+ this.logger(` Adding check constraint "${spec.name}" to ${qualifiedTableName}...`);
1531
+ await this.client.query((0, check_constraint_sql_1.buildAddCheckConstraintStatement)(spec, qualifiedTableName));
1532
+ this.logger(` ✓ Check constraint "${spec.name}" added
1533
+ `);
1534
+ }
1535
+ /**
1536
+ * Names of the CHECK constraints (`contype = 'c'`) defined on a table,
1537
+ * resolved via the table's namespace so the lookup is independent of
1538
+ * search_path. Includes column NOT NULL-style checks PostgreSQL stores as
1539
+ * table constraints, which is fine — reconciliation only tests declared
1540
+ * names for membership.
1541
+ */
1542
+ async getExistingCheckConstraints(tableName, schemaName) {
1543
+ const result = await this.client.query(`
1544
+ SELECT con.conname
1545
+ FROM pg_constraint con
1546
+ JOIN pg_class c ON c.oid = con.conrelid
1547
+ JOIN pg_namespace n ON n.oid = c.relnamespace
1548
+ WHERE n.nspname = $1
1549
+ AND c.relname = $2
1550
+ AND con.contype = 'c'
1551
+ `, [schemaName || 'public', tableName]);
1552
+ return result.rows.map((row) => row.conname);
1553
+ }
1460
1554
  /**
1461
1555
  * Execute create foreign key
1462
1556
  */
@@ -1499,10 +1593,10 @@ class DbSchemaManager {
1499
1593
  }
1500
1594
  const tables = new Map();
1501
1595
  for (const schemaName of schemas) {
1502
- const result = await this.client.query(`
1503
- SELECT table_name
1504
- FROM information_schema.tables
1505
- WHERE table_schema = $1 AND table_type = 'BASE TABLE'
1596
+ const result = await this.client.query(`
1597
+ SELECT table_name
1598
+ FROM information_schema.tables
1599
+ WHERE table_schema = $1 AND table_type = 'BASE TABLE'
1506
1600
  `, [schemaName]);
1507
1601
  for (const row of result.rows) {
1508
1602
  // Use composite key for non-public schemas to avoid collisions
@@ -1516,20 +1610,20 @@ class DbSchemaManager {
1516
1610
  * Get all columns for a table
1517
1611
  */
1518
1612
  async getExistingColumns(tableName, schemaName) {
1519
- const result = await this.client.query(`
1520
- SELECT
1521
- column_name,
1522
- data_type,
1523
- udt_name,
1524
- character_maximum_length,
1525
- numeric_precision,
1526
- numeric_scale,
1527
- is_nullable,
1528
- column_default,
1529
- collation_name
1530
- FROM information_schema.columns
1531
- WHERE table_schema = $1 AND table_name = $2
1532
- ORDER BY ordinal_position
1613
+ const result = await this.client.query(`
1614
+ SELECT
1615
+ column_name,
1616
+ data_type,
1617
+ udt_name,
1618
+ character_maximum_length,
1619
+ numeric_precision,
1620
+ numeric_scale,
1621
+ is_nullable,
1622
+ column_default,
1623
+ collation_name
1624
+ FROM information_schema.columns
1625
+ WHERE table_schema = $1 AND table_name = $2
1626
+ ORDER BY ordinal_position
1533
1627
  `, [schemaName || 'public', tableName]);
1534
1628
  const columns = new Map();
1535
1629
  for (const row of result.rows) {
@@ -1544,24 +1638,24 @@ class DbSchemaManager {
1544
1638
  // `pg_get_indexdef(oid, 0, true)` yields the canonical, pretty-printed
1545
1639
  // CREATE INDEX statement (no CONCURRENTLY / IF NOT EXISTS, redundant parens
1546
1640
  // stripped), which the signature comparison parses to detect changes.
1547
- const result = await this.client.query(`
1548
- SELECT
1549
- i.relname as index_name,
1550
- pg_get_indexdef(i.oid, 0, true) as canonical_def,
1551
- ARRAY(
1552
- SELECT a.attname
1553
- FROM unnest(ix.indkey) WITH ORDINALITY k(attnum, ord)
1554
- JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = k.attnum
1555
- ORDER BY k.ord
1556
- ) as column_names
1557
- FROM pg_index ix
1558
- JOIN pg_class t ON t.oid = ix.indrelid
1559
- JOIN pg_class i ON i.oid = ix.indexrelid
1560
- JOIN pg_namespace n ON n.oid = t.relnamespace
1561
- WHERE
1562
- n.nspname = $1
1563
- AND t.relname = $2
1564
- AND NOT ix.indisprimary
1641
+ const result = await this.client.query(`
1642
+ SELECT
1643
+ i.relname as index_name,
1644
+ pg_get_indexdef(i.oid, 0, true) as canonical_def,
1645
+ ARRAY(
1646
+ SELECT a.attname
1647
+ FROM unnest(ix.indkey) WITH ORDINALITY k(attnum, ord)
1648
+ JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = k.attnum
1649
+ ORDER BY k.ord
1650
+ ) as column_names
1651
+ FROM pg_index ix
1652
+ JOIN pg_class t ON t.oid = ix.indrelid
1653
+ JOIN pg_class i ON i.oid = ix.indexrelid
1654
+ JOIN pg_namespace n ON n.oid = t.relnamespace
1655
+ WHERE
1656
+ n.nspname = $1
1657
+ AND t.relname = $2
1658
+ AND NOT ix.indisprimary
1565
1659
  `, [schemaName || 'public', tableName]);
1566
1660
  return result.rows.map(row => ({
1567
1661
  index_name: row.index_name,
@@ -1641,29 +1735,29 @@ class DbSchemaManager {
1641
1735
  * Get all foreign key constraints for a table
1642
1736
  */
1643
1737
  async getExistingForeignKeys(tableName, schemaName) {
1644
- const result = await this.client.query(`
1645
- SELECT
1646
- tc.constraint_name,
1647
- array_agg(kcu.column_name ORDER BY kcu.ordinal_position) as column_names,
1648
- ccu.table_name AS referenced_table,
1649
- array_agg(ccu.column_name ORDER BY kcu.ordinal_position) as referenced_column_names,
1650
- rc.delete_rule as on_delete,
1651
- rc.update_rule as on_update
1652
- FROM information_schema.table_constraints AS tc
1653
- JOIN information_schema.key_column_usage AS kcu
1654
- ON tc.constraint_name = kcu.constraint_name
1655
- AND tc.table_schema = kcu.table_schema
1656
- JOIN information_schema.constraint_column_usage AS ccu
1657
- ON ccu.constraint_name = tc.constraint_name
1658
- AND ccu.table_schema = tc.table_schema
1659
- JOIN information_schema.referential_constraints AS rc
1660
- ON rc.constraint_name = tc.constraint_name
1661
- AND rc.constraint_schema = tc.table_schema
1662
- WHERE
1663
- tc.table_schema = $1
1664
- AND tc.table_name = $2
1665
- AND tc.constraint_type = 'FOREIGN KEY'
1666
- GROUP BY tc.constraint_name, ccu.table_name, rc.delete_rule, rc.update_rule
1738
+ const result = await this.client.query(`
1739
+ SELECT
1740
+ tc.constraint_name,
1741
+ array_agg(kcu.column_name ORDER BY kcu.ordinal_position) as column_names,
1742
+ ccu.table_name AS referenced_table,
1743
+ array_agg(ccu.column_name ORDER BY kcu.ordinal_position) as referenced_column_names,
1744
+ rc.delete_rule as on_delete,
1745
+ rc.update_rule as on_update
1746
+ FROM information_schema.table_constraints AS tc
1747
+ JOIN information_schema.key_column_usage AS kcu
1748
+ ON tc.constraint_name = kcu.constraint_name
1749
+ AND tc.table_schema = kcu.table_schema
1750
+ JOIN information_schema.constraint_column_usage AS ccu
1751
+ ON ccu.constraint_name = tc.constraint_name
1752
+ AND ccu.table_schema = tc.table_schema
1753
+ JOIN information_schema.referential_constraints AS rc
1754
+ ON rc.constraint_name = tc.constraint_name
1755
+ AND rc.constraint_schema = tc.table_schema
1756
+ WHERE
1757
+ tc.table_schema = $1
1758
+ AND tc.table_name = $2
1759
+ AND tc.constraint_type = 'FOREIGN KEY'
1760
+ GROUP BY tc.constraint_name, ccu.table_name, rc.delete_rule, rc.update_rule
1667
1761
  `, [schemaName || 'public', tableName]);
1668
1762
  return result.rows.map(row => ({
1669
1763
  constraint_name: row.constraint_name,
@@ -1894,6 +1988,8 @@ class DbSchemaManager {
1894
1988
  const kindsDesc = operation.kinds && operation.kinds.length > 0 ? ` (${operation.kinds.join(', ')})` : '';
1895
1989
  return `Create statistics "${operation.statisticsName}"${kindsDesc} on "${operation.tableName}" (${operation.expressions.join(', ')})`;
1896
1990
  }
1991
+ case 'create_check_constraint':
1992
+ return `Add check constraint "${operation.constraintName}" on "${operation.tableName}" CHECK (${operation.expression})`;
1897
1993
  case 'set_database_setting':
1898
1994
  return `Set database parameter ${operation.name} = ${operation.value} (ALTER DATABASE ... SET, persists for new sessions)`;
1899
1995
  case 'create_foreign_key':
@@ -1969,15 +2065,15 @@ exports.DbSchemaManager = DbSchemaManager;
1969
2065
  * `search_path`, so an unqualified `'unaccent'::regdictionary` would fail with
1970
2066
  * "text search dictionary unaccent does not exist" when the index is built.
1971
2067
  */
1972
- DbSchemaManager.SEARCH_NORMALIZE_FUNCTION_SQL = `
1973
- CREATE OR REPLACE FUNCTION public.search_normalize(value text)
1974
- RETURNS text
1975
- LANGUAGE sql
1976
- IMMUTABLE
1977
- PARALLEL SAFE
1978
- RETURNS NULL ON NULL INPUT
1979
- AS $$
1980
- SELECT lower(public.unaccent('public.unaccent', value))
2068
+ DbSchemaManager.SEARCH_NORMALIZE_FUNCTION_SQL = `
2069
+ CREATE OR REPLACE FUNCTION public.search_normalize(value text)
2070
+ RETURNS text
2071
+ LANGUAGE sql
2072
+ IMMUTABLE
2073
+ PARALLEL SAFE
2074
+ RETURNS NULL ON NULL INPUT
2075
+ AS $$
2076
+ SELECT lower(public.unaccent('public.unaccent', value))
1981
2077
  $$`;
1982
2078
  /**
1983
2079
  * Execute create index