@smartive/graphql-magic 23.7.0-next.5 → 23.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,5 @@
1
- ## [23.7.0-next.5](https://github.com/smartive/graphql-magic/compare/v23.7.0-next.4...v23.7.0-next.5) (2026-03-04)
1
+ ## [23.7.0](https://github.com/smartive/graphql-magic/compare/v23.6.1...v23.7.0) (2026-03-04)
2
2
 
3
- ### Bug Fixes
3
+ ### Features
4
4
 
5
- * constraint normalization for empty migration when schema in sync ([1a8e419](https://github.com/smartive/graphql-magic/commit/1a8e419f82899a9045f08ab96dbc9249e49dd773))
5
+ * Enhance check constraint handling in migration generator ([#430](https://github.com/smartive/graphql-magic/issues/430)) ([b23a885](https://github.com/smartive/graphql-magic/commit/b23a8858ec46182b90cdb220e444c782f46201fc))
package/dist/bin/gqm.cjs CHANGED
@@ -389,8 +389,6 @@ var EntityModel = class extends Model {
389
389
  for (const constraint of this.constraints) {
390
390
  if (constraint.kind === "check") {
391
391
  validateCheckConstraint(this, constraint);
392
- } else if (constraint.kind === "exclude") {
393
- validateExcludeConstraint(this, constraint);
394
392
  }
395
393
  }
396
394
  }
@@ -646,19 +644,6 @@ var validateCheckConstraint = (model, constraint) => {
646
644
  }
647
645
  }
648
646
  };
649
- var validateExcludeConstraint = (model, constraint) => {
650
- const validColumnNames = new Set(model.fields.map((f) => getColumnName(f)));
651
- for (const el of constraint.elements) {
652
- if ("column" in el) {
653
- if (!validColumnNames.has(el.column)) {
654
- const validList = [...validColumnNames].sort().join(", ");
655
- throw new Error(
656
- `Exclude constraint "${constraint.name}" references column "${el.column}" which does not exist on model ${model.name}. Valid columns: ${validList}`
657
- );
658
- }
659
- }
660
- }
661
- };
662
647
 
663
648
  // src/db/generate.ts
664
649
  var import_code_block_writer = __toESM(require("code-block-writer"), 1);
@@ -807,7 +792,6 @@ var generateFunctionsFromDatabase = async (knex2) => {
807
792
  JOIN pg_namespace n ON p.pronamespace = n.oid
808
793
  WHERE n.nspname = 'public'
809
794
  AND NOT EXISTS (SELECT 1 FROM pg_aggregate a WHERE a.aggfnoid = p.oid)
810
- AND NOT EXISTS (SELECT 1 FROM pg_depend d WHERE d.objid = p.oid AND d.deptype = 'e')
811
795
  ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
812
796
  `);
813
797
  const aggregateFunctions = await knex2.raw(`
@@ -822,7 +806,6 @@ var generateFunctionsFromDatabase = async (knex2) => {
822
806
  JOIN pg_aggregate a ON p.oid = a.aggfnoid
823
807
  JOIN pg_namespace n ON p.pronamespace = n.oid
824
808
  WHERE n.nspname = 'public'
825
- AND NOT EXISTS (SELECT 1 FROM pg_depend d WHERE d.objid = p.oid AND d.deptype = 'e')
826
809
  ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
827
810
  `);
828
811
  const functions = [];
@@ -927,7 +910,6 @@ var getDatabaseFunctions = async (knex2) => {
927
910
  JOIN pg_namespace n ON p.pronamespace = n.oid
928
911
  WHERE n.nspname = 'public'
929
912
  AND NOT EXISTS (SELECT 1 FROM pg_aggregate a WHERE a.aggfnoid = p.oid)
930
- AND NOT EXISTS (SELECT 1 FROM pg_depend d WHERE d.objid = p.oid AND d.deptype = 'e')
931
913
  ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
932
914
  `);
933
915
  const aggregateFunctions = await knex2.raw(`
@@ -943,7 +925,6 @@ var getDatabaseFunctions = async (knex2) => {
943
925
  JOIN pg_aggregate a ON p.oid = a.aggfnoid
944
926
  JOIN pg_namespace n ON p.pronamespace = n.oid
945
927
  WHERE n.nspname = 'public'
946
- AND NOT EXISTS (SELECT 1 FROM pg_depend d WHERE d.objid = p.oid AND d.deptype = 'e')
947
928
  ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
948
929
  `);
949
930
  const result = [];
@@ -1087,7 +1068,7 @@ Summary: ${updatedCount} updated, ${skippedCount} skipped`);
1087
1068
  };
1088
1069
 
1089
1070
  // src/migrations/generate.ts
1090
- var MigrationGenerator = class _MigrationGenerator {
1071
+ var MigrationGenerator = class {
1091
1072
  constructor(knex2, models, parsedFunctions) {
1092
1073
  this.models = models;
1093
1074
  this.parsedFunctions = parsedFunctions;
@@ -1103,10 +1084,6 @@ var MigrationGenerator = class _MigrationGenerator {
1103
1084
  columns = {};
1104
1085
  /** table name -> constraint name -> check clause expression */
1105
1086
  existingCheckConstraints = {};
1106
- /** table name -> constraint name -> { normalized, raw } */
1107
- existingExcludeConstraints = {};
1108
- /** table name -> constraint name -> { normalized, raw } */
1109
- existingConstraintTriggers = {};
1110
1087
  uuidUsed;
1111
1088
  nowUsed;
1112
1089
  needsMigration = false;
@@ -1134,56 +1111,8 @@ var MigrationGenerator = class _MigrationGenerator {
1134
1111
  }
1135
1112
  this.existingCheckConstraints[tableName].set(row.constraint_name, row.check_clause);
1136
1113
  }
1137
- const excludeResult = await schema.knex.raw(
1138
- `SELECT c.conrelid::regclass::text as table_name, c.conname as constraint_name, pg_get_constraintdef(c.oid) as constraint_def
1139
- FROM pg_constraint c
1140
- JOIN pg_namespace n ON c.connamespace = n.oid
1141
- WHERE n.nspname = 'public' AND c.contype = 'x'`
1142
- );
1143
- const excludeRows = "rows" in excludeResult && Array.isArray(excludeResult.rows) ? excludeResult.rows : [];
1144
- for (const row of excludeRows) {
1145
- const tableName = row.table_name.split(".").pop()?.replace(/^"|"$/g, "") ?? row.table_name;
1146
- if (!this.existingExcludeConstraints[tableName]) {
1147
- this.existingExcludeConstraints[tableName] = /* @__PURE__ */ new Map();
1148
- }
1149
- this.existingExcludeConstraints[tableName].set(row.constraint_name, {
1150
- normalized: this.normalizeExcludeDef(row.constraint_def),
1151
- raw: row.constraint_def
1152
- });
1153
- }
1154
- const triggerResult = await schema.knex.raw(
1155
- `SELECT c.conrelid::regclass::text as table_name, c.conname as constraint_name, pg_get_triggerdef(t.oid) as trigger_def
1156
- FROM pg_constraint c
1157
- JOIN pg_trigger t ON t.tgconstraint = c.oid
1158
- JOIN pg_namespace n ON c.connamespace = n.oid
1159
- WHERE n.nspname = 'public' AND c.contype = 't'`
1160
- );
1161
- const triggerRows = "rows" in triggerResult && Array.isArray(triggerResult.rows) ? triggerResult.rows : [];
1162
- for (const row of triggerRows) {
1163
- const tableName = row.table_name.split(".").pop()?.replace(/^"|"$/g, "") ?? row.table_name;
1164
- if (!this.existingConstraintTriggers[tableName]) {
1165
- this.existingConstraintTriggers[tableName] = /* @__PURE__ */ new Map();
1166
- }
1167
- this.existingConstraintTriggers[tableName].set(row.constraint_name, {
1168
- normalized: this.normalizeTriggerDef(row.trigger_def),
1169
- raw: row.trigger_def
1170
- });
1171
- }
1172
1114
  const up = [];
1173
1115
  const down = [];
1174
- const wantsBtreeGist = models.entities.some(
1175
- (model) => model.constraints?.some((c) => c.kind === "exclude" && c.elements.some((el) => "column" in el && el.operator === "="))
1176
- );
1177
- if (wantsBtreeGist) {
1178
- const extResult = await schema.knex("pg_extension").where("extname", "btree_gist").select("oid").first();
1179
- const btreeGistInstalled = !!extResult;
1180
- if (!btreeGistInstalled) {
1181
- up.unshift(() => {
1182
- this.writer.writeLine(`await knex.raw('CREATE EXTENSION IF NOT EXISTS btree_gist');`);
1183
- this.writer.blankLine();
1184
- });
1185
- }
1186
- }
1187
1116
  this.createEnums(
1188
1117
  this.models.enums.filter((enm2) => !enums.includes((0, import_lowerFirst.default)(enm2.name))),
1189
1118
  up,
@@ -1262,22 +1191,10 @@ var MigrationGenerator = class _MigrationGenerator {
1262
1191
  if (entry.kind === "check") {
1263
1192
  validateCheckConstraint(model, entry);
1264
1193
  const table = model.name;
1265
- const constraintName = this.getConstraintName(model, entry, i);
1266
- up.push(() => {
1267
- this.addCheckConstraint(table, constraintName, entry.expression, entry.deferrable);
1268
- });
1269
- } else if (entry.kind === "exclude") {
1270
- validateExcludeConstraint(model, entry);
1271
- const table = model.name;
1272
- const constraintName = this.getConstraintName(model, entry, i);
1273
- up.push(() => {
1274
- this.addExcludeConstraint(table, constraintName, entry);
1275
- });
1276
- } else if (entry.kind === "constraint_trigger") {
1277
- const table = model.name;
1278
- const constraintName = this.getConstraintName(model, entry, i);
1194
+ const constraintName = this.getCheckConstraintName(model, entry, i);
1195
+ const expression = entry.expression;
1279
1196
  up.push(() => {
1280
- this.addConstraintTrigger(table, constraintName, entry);
1197
+ this.addCheckConstraint(table, constraintName, expression);
1281
1198
  });
1282
1199
  }
1283
1200
  }
@@ -1317,81 +1234,36 @@ var MigrationGenerator = class _MigrationGenerator {
1317
1234
  );
1318
1235
  this.updateFields(model, existingFields, up, down);
1319
1236
  if (model.constraints?.length) {
1320
- const existingCheckMap = this.existingCheckConstraints[model.name];
1321
- const existingExcludeMap = this.existingExcludeConstraints[model.name];
1322
- const existingTriggerMap = this.existingConstraintTriggers[model.name];
1323
1237
  for (let i = 0; i < model.constraints.length; i++) {
1324
1238
  const entry = model.constraints[i];
1239
+ if (entry.kind !== "check") {
1240
+ continue;
1241
+ }
1242
+ validateCheckConstraint(model, entry);
1325
1243
  const table = model.name;
1326
- const constraintName = this.getConstraintName(model, entry, i);
1327
- if (entry.kind === "check") {
1328
- validateCheckConstraint(model, entry);
1329
- const newExpression = entry.expression;
1330
- const existingExpression = existingCheckMap?.get(constraintName);
1331
- if (existingExpression === void 0) {
1332
- up.push(() => {
1333
- this.addCheckConstraint(table, constraintName, newExpression, entry.deferrable);
1334
- });
1335
- down.push(() => {
1336
- this.dropCheckConstraint(table, constraintName);
1337
- });
1338
- } else if (this.normalizeCheckExpression(existingExpression) !== this.normalizeCheckExpression(newExpression)) {
1339
- up.push(() => {
1340
- this.dropCheckConstraint(table, constraintName);
1341
- this.addCheckConstraint(table, constraintName, newExpression, entry.deferrable);
1342
- });
1343
- down.push(() => {
1344
- this.dropCheckConstraint(table, constraintName);
1345
- this.addCheckConstraint(table, constraintName, existingExpression);
1346
- });
1347
- }
1348
- } else if (entry.kind === "exclude") {
1349
- validateExcludeConstraint(model, entry);
1350
- const newDef = this.normalizeExcludeDef(this.buildExcludeDef(entry));
1351
- const existing = existingExcludeMap?.get(constraintName);
1352
- if (existing === void 0) {
1353
- up.push(() => {
1354
- this.addExcludeConstraint(table, constraintName, entry);
1355
- });
1356
- down.push(() => {
1357
- this.dropExcludeConstraint(table, constraintName);
1358
- });
1359
- } else if (existing.normalized !== newDef) {
1360
- up.push(() => {
1361
- this.dropExcludeConstraint(table, constraintName);
1362
- this.addExcludeConstraint(table, constraintName, entry);
1363
- });
1364
- down.push(() => {
1365
- this.dropExcludeConstraint(table, constraintName);
1366
- const escaped = this.escapeExpressionForRaw(existing.raw);
1367
- this.writer.writeLine(
1368
- `await knex.raw(\`ALTER TABLE "${table}" ADD CONSTRAINT "${constraintName}" ${escaped}\`);`
1369
- );
1370
- this.writer.blankLine();
1371
- });
1372
- }
1373
- } else if (entry.kind === "constraint_trigger") {
1374
- const newDef = this.normalizeTriggerDef(this.buildConstraintTriggerDef(table, constraintName, entry));
1375
- const existing = existingTriggerMap?.get(constraintName);
1376
- if (existing === void 0) {
1377
- up.push(() => {
1378
- this.addConstraintTrigger(table, constraintName, entry);
1379
- });
1380
- down.push(() => {
1381
- this.dropConstraintTrigger(table, constraintName);
1382
- });
1383
- } else if (existing.normalized !== newDef) {
1384
- up.push(() => {
1385
- this.dropConstraintTrigger(table, constraintName);
1386
- this.addConstraintTrigger(table, constraintName, entry);
1387
- });
1388
- down.push(() => {
1389
- this.dropConstraintTrigger(table, constraintName);
1390
- const escaped = existing.raw.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$");
1391
- this.writer.writeLine(`await knex.raw(\`${escaped}\`);`);
1392
- this.writer.blankLine();
1393
- });
1394
- }
1244
+ const constraintName = this.getCheckConstraintName(model, entry, i);
1245
+ const existingConstraint = this.findExistingConstraint(table, entry, constraintName);
1246
+ if (!existingConstraint) {
1247
+ up.push(() => {
1248
+ this.addCheckConstraint(table, constraintName, entry.expression);
1249
+ });
1250
+ down.push(() => {
1251
+ this.dropCheckConstraint(table, constraintName);
1252
+ });
1253
+ } else if (!await this.equalExpressions(
1254
+ table,
1255
+ existingConstraint.constraintName,
1256
+ existingConstraint.expression,
1257
+ entry.expression
1258
+ )) {
1259
+ up.push(() => {
1260
+ this.dropCheckConstraint(table, existingConstraint.constraintName);
1261
+ this.addCheckConstraint(table, constraintName, entry.expression);
1262
+ });
1263
+ down.push(() => {
1264
+ this.dropCheckConstraint(table, constraintName);
1265
+ this.addCheckConstraint(table, existingConstraint.constraintName, existingConstraint.expression);
1266
+ });
1395
1267
  }
1396
1268
  }
1397
1269
  }
@@ -1766,122 +1638,148 @@ var MigrationGenerator = class _MigrationGenerator {
1766
1638
  renameColumn(from, to) {
1767
1639
  this.writer.writeLine(`table.renameColumn('${from}', '${to}');`);
1768
1640
  }
1769
- getConstraintName(model, entry, index) {
1641
+ getCheckConstraintName(model, entry, index) {
1770
1642
  return `${model.name}_${entry.name}_${entry.kind}_${index}`;
1771
1643
  }
1772
- static SQL_KEYWORDS = /* @__PURE__ */ new Set([
1773
- "and",
1774
- "or",
1775
- "not",
1776
- "in",
1777
- "is",
1778
- "null",
1779
- "true",
1780
- "false",
1781
- "between",
1782
- "like",
1783
- "exists",
1784
- "all",
1785
- "any",
1786
- "asc",
1787
- "desc",
1788
- "with",
1789
- "using",
1790
- "as",
1791
- "on",
1792
- "infinity",
1793
- "extract",
1794
- "current_date",
1795
- "current_timestamp"
1796
- ]);
1797
- static LITERAL_PLACEHOLDER = "\uE000";
1798
- normalizeSqlIdentifiers(s) {
1799
- const literals = [];
1800
- let result = s.replace(/'([^']|'')*'/g, (lit) => {
1801
- literals.push(lit);
1802
- return `${_MigrationGenerator.LITERAL_PLACEHOLDER}${literals.length - 1}${_MigrationGenerator.LITERAL_PLACEHOLDER}`;
1803
- });
1804
- result = result.replace(/"([^"]*)"/g, (_, ident) => `"${ident.toLowerCase()}"`);
1805
- result = result.replace(
1806
- /\b([a-zA-Z_][a-zA-Z0-9_]*)\b(?!\s*\()/g,
1807
- (match) => _MigrationGenerator.SQL_KEYWORDS.has(match.toLowerCase()) ? match : `"${match.toLowerCase()}"`
1808
- );
1809
- for (let i = 0; i < literals.length; i++) {
1810
- result = result.replace(
1811
- new RegExp(`${_MigrationGenerator.LITERAL_PLACEHOLDER}${i}${_MigrationGenerator.LITERAL_PLACEHOLDER}`, "g"),
1812
- literals[i]
1813
- );
1644
+ normalizeCheckExpression(expr) {
1645
+ let normalized = expr.replace(/\s+/g, " ").trim();
1646
+ while (this.isWrappedByOuterParentheses(normalized)) {
1647
+ normalized = normalized.slice(1, -1).trim();
1814
1648
  }
1815
- return result;
1649
+ return normalized;
1816
1650
  }
1817
- stripOuterParens(s) {
1818
- while (s.length >= 2 && s.startsWith("(") && s.endsWith(")")) {
1819
- let depth = 0;
1820
- let match = true;
1821
- for (let i = 0; i < s.length; i++) {
1822
- if (s[i] === "(") {
1823
- depth++;
1824
- } else if (s[i] === ")") {
1825
- depth--;
1651
+ isWrappedByOuterParentheses(expr) {
1652
+ if (!expr.startsWith("(") || !expr.endsWith(")")) {
1653
+ return false;
1654
+ }
1655
+ let depth = 0;
1656
+ let inSingleQuote = false;
1657
+ for (let i = 0; i < expr.length; i++) {
1658
+ const char = expr[i];
1659
+ const next = expr[i + 1];
1660
+ if (char === "'") {
1661
+ if (inSingleQuote && next === "'") {
1662
+ i++;
1663
+ continue;
1826
1664
  }
1827
- if (depth === 0 && i < s.length - 1) {
1828
- match = false;
1829
- break;
1665
+ inSingleQuote = !inSingleQuote;
1666
+ continue;
1667
+ }
1668
+ if (inSingleQuote) {
1669
+ continue;
1670
+ }
1671
+ if (char === "(") {
1672
+ depth++;
1673
+ } else if (char === ")") {
1674
+ depth--;
1675
+ if (depth === 0 && i !== expr.length - 1) {
1676
+ return false;
1677
+ }
1678
+ if (depth < 0) {
1679
+ return false;
1830
1680
  }
1831
1681
  }
1832
- if (!match || depth !== 0) {
1833
- break;
1682
+ }
1683
+ return depth === 0;
1684
+ }
1685
+ findExistingConstraint(table, entry, preferredConstraintName) {
1686
+ const existingMap = this.existingCheckConstraints[table];
1687
+ if (!existingMap) {
1688
+ return null;
1689
+ }
1690
+ const preferredExpression = existingMap.get(preferredConstraintName);
1691
+ if (preferredExpression !== void 0) {
1692
+ return {
1693
+ constraintName: preferredConstraintName,
1694
+ expression: preferredExpression
1695
+ };
1696
+ }
1697
+ const normalizedNewExpression = this.normalizeCheckExpression(entry.expression);
1698
+ const constraintPrefix = `${table}_${entry.name}_${entry.kind}_`;
1699
+ for (const [constraintName, expression] of existingMap.entries()) {
1700
+ if (!constraintName.startsWith(constraintPrefix)) {
1701
+ continue;
1702
+ }
1703
+ if (this.normalizeCheckExpression(expression) !== normalizedNewExpression) {
1704
+ continue;
1834
1705
  }
1835
- s = s.slice(1, -1).trim();
1706
+ return { constraintName, expression };
1836
1707
  }
1837
- return s;
1708
+ return null;
1838
1709
  }
1839
- splitAtTopLevel(s, sep) {
1840
- const parts = [];
1841
- let depth = 0;
1842
- let start = 0;
1843
- const sepLen = sep.length;
1844
- for (let i = 0; i <= s.length - sepLen; i++) {
1845
- if (s[i] === "(") {
1846
- depth++;
1847
- } else if (s[i] === ")") {
1848
- depth--;
1710
+ async equalExpressions(table, constraintName, existingExpression, newExpression) {
1711
+ try {
1712
+ const [canonicalExisting, canonicalNew] = await Promise.all([
1713
+ this.canonicalizeCheckExpressionWithPostgres(table, existingExpression),
1714
+ this.canonicalizeCheckExpressionWithPostgres(table, newExpression)
1715
+ ]);
1716
+ return canonicalExisting === canonicalNew;
1717
+ } catch (error) {
1718
+ console.warn(
1719
+ `Failed to canonicalize check constraint "${constraintName}" on table "${table}". Treating it as changed.`,
1720
+ error
1721
+ );
1722
+ return false;
1723
+ }
1724
+ }
1725
+ async canonicalizeCheckExpressionWithPostgres(table, expression) {
1726
+ const sourceTableIdentifier = table.split(".").map((part) => this.quoteIdentifier(part)).join(".");
1727
+ const uniqueSuffix = `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
1728
+ const tableSlug = table.toLowerCase().replace(/[^a-z0-9_]/g, "_");
1729
+ const tempTableName = `gqm_tmp_check_${tableSlug}_${uniqueSuffix}`;
1730
+ const tempTableIdentifier = this.quoteIdentifier(tempTableName);
1731
+ const constraintName = `gqm_tmp_check_${uniqueSuffix}`;
1732
+ const constraintIdentifier = this.quoteIdentifier(constraintName);
1733
+ const trx = await this.knex.transaction();
1734
+ try {
1735
+ await trx.raw(`CREATE TEMP TABLE ${tempTableIdentifier} (LIKE ${sourceTableIdentifier}) ON COMMIT DROP`);
1736
+ await trx.raw(`ALTER TABLE ${tempTableIdentifier} ADD CONSTRAINT ${constraintIdentifier} CHECK (${expression})`);
1737
+ const result = await trx.raw(
1738
+ `SELECT pg_get_constraintdef(c.oid, true) AS constraint_definition
1739
+ FROM pg_constraint c
1740
+ JOIN pg_class t
1741
+ ON t.oid = c.conrelid
1742
+ WHERE t.relname = ?
1743
+ AND c.conname = ?
1744
+ ORDER BY c.oid DESC
1745
+ LIMIT 1`,
1746
+ [tempTableName, constraintName]
1747
+ );
1748
+ const rows = "rows" in result && Array.isArray(result.rows) ? result.rows : [];
1749
+ const definition = rows[0]?.constraint_definition;
1750
+ if (!definition) {
1751
+ throw new Error(`Could not read canonical check definition for expression: ${expression}`);
1849
1752
  }
1850
- if (depth === 0 && s.slice(i, i + sepLen).toLowerCase() === sep.toLowerCase()) {
1851
- parts.push(s.slice(start, i).trim());
1852
- start = i + sepLen;
1853
- i += sepLen - 1;
1753
+ return this.normalizeCheckExpression(this.extractCheckExpressionFromDefinition(definition));
1754
+ } finally {
1755
+ try {
1756
+ await trx.rollback();
1757
+ } catch {
1854
1758
  }
1855
1759
  }
1856
- parts.push(s.slice(start).trim());
1857
- return parts.filter(Boolean);
1858
1760
  }
1859
- normalizeCheckExpression(expr) {
1860
- let s = expr.replace(/\s+/g, " ").trim();
1861
- const normalizeParts = (str, separator) => this.splitAtTopLevel(str, separator).map((p) => this.stripOuterParens(p)).join(separator);
1862
- s = this.stripOuterParens(s);
1863
- s = normalizeParts(s, " OR ");
1864
- s = this.stripOuterParens(s);
1865
- s = normalizeParts(s, " AND ");
1866
- s = s.replace(/\s*\(\s*/g, "(").replace(/\s*\)\s*/g, ")").replace(/\s+AND\s+/gi, " AND ").replace(/\s+OR\s+/gi, " OR ").trim();
1867
- return this.normalizeSqlIdentifiers(s);
1868
- }
1869
- normalizeExcludeDef(def) {
1870
- const s = def.replace(/\s+/g, " ").replace(/\s*\(\s*/g, "(").replace(/\s*\)\s*/g, ")").trim();
1871
- return this.normalizeSqlIdentifiers(s);
1872
- }
1873
- normalizeTriggerDef(def) {
1874
- return def.replace(/\s+/g, " ").replace(/\s*\(\s*/g, "(").replace(/\s*\)\s*/g, ")").replace(/\bON\s+[a-zA-Z_][a-zA-Z0-9_]*\./gi, "ON ").trim();
1761
+ extractCheckExpressionFromDefinition(definition) {
1762
+ const trimmed = definition.trim();
1763
+ const match = trimmed.match(/^CHECK\s*\(([\s\S]*)\)$/i);
1764
+ if (!match) {
1765
+ return trimmed;
1766
+ }
1767
+ return match[1];
1768
+ }
1769
+ quoteIdentifier(identifier) {
1770
+ return `"${identifier.replace(/"/g, '""')}"`;
1771
+ }
1772
+ quoteQualifiedIdentifier(identifier) {
1773
+ return identifier.split(".").map((part) => this.quoteIdentifier(part)).join(".");
1875
1774
  }
1876
1775
  /** Escape expression for embedding inside a template literal in generated code */
1877
1776
  escapeExpressionForRaw(expr) {
1878
1777
  return expr.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$");
1879
1778
  }
1880
- addCheckConstraint(table, constraintName, expression, deferrable) {
1779
+ addCheckConstraint(table, constraintName, expression) {
1881
1780
  const escaped = this.escapeExpressionForRaw(expression);
1882
- const deferrableClause = deferrable ? ` DEFERRABLE ${deferrable}` : "";
1883
1781
  this.writer.writeLine(
1884
- `await knex.raw(\`ALTER TABLE "${table}" ADD CONSTRAINT "${constraintName}" CHECK (${escaped})${deferrableClause}\`);`
1782
+ `await knex.raw(\`ALTER TABLE "${table}" ADD CONSTRAINT "${constraintName}" CHECK (${escaped})\`);`
1885
1783
  );
1886
1784
  this.writer.blankLine();
1887
1785
  }
@@ -1889,43 +1787,6 @@ var MigrationGenerator = class _MigrationGenerator {
1889
1787
  this.writer.writeLine(`await knex.raw('ALTER TABLE "${table}" DROP CONSTRAINT "${constraintName}"');`);
1890
1788
  this.writer.blankLine();
1891
1789
  }
1892
- buildExcludeDef(entry) {
1893
- const elementsStr = entry.elements.map((el) => "column" in el ? `"${el.column}" WITH ${el.operator}` : `${el.expression} WITH ${el.operator}`).join(", ");
1894
- const whereClause = entry.where ? ` WHERE (${entry.where})` : "";
1895
- const deferrableClause = entry.deferrable ? ` DEFERRABLE ${entry.deferrable}` : "";
1896
- return `EXCLUDE USING ${entry.using} (${elementsStr})${whereClause}${deferrableClause}`;
1897
- }
1898
- addExcludeConstraint(table, constraintName, entry) {
1899
- const def = this.buildExcludeDef(entry);
1900
- const escaped = this.escapeExpressionForRaw(def);
1901
- this.writer.writeLine(`await knex.raw(\`ALTER TABLE "${table}" ADD CONSTRAINT "${constraintName}" ${escaped}\`);`);
1902
- this.writer.blankLine();
1903
- }
1904
- dropExcludeConstraint(table, constraintName) {
1905
- this.writer.writeLine(`await knex.raw('ALTER TABLE "${table}" DROP CONSTRAINT "${constraintName}"');`);
1906
- this.writer.blankLine();
1907
- }
1908
- buildConstraintTriggerDef(table, constraintName, entry) {
1909
- const eventsStr = entry.events.join(" OR ");
1910
- const deferrableClause = entry.deferrable ? ` DEFERRABLE ${entry.deferrable}` : "";
1911
- const argsStr = entry.function.args?.length ? entry.function.args.map((a) => `"${a}"`).join(", ") : "";
1912
- const executeClause = argsStr ? `EXECUTE FUNCTION ${entry.function.name}(${argsStr})` : `EXECUTE FUNCTION ${entry.function.name}()`;
1913
- return `CREATE CONSTRAINT TRIGGER "${constraintName}" ${entry.when} ${eventsStr} ON "${table}"${deferrableClause} FOR EACH ${entry.forEach} ${executeClause}`;
1914
- }
1915
- addConstraintTrigger(table, constraintName, entry) {
1916
- const eventsStr = entry.events.join(" OR ");
1917
- const deferrableClause = entry.deferrable ? ` DEFERRABLE ${entry.deferrable}` : "";
1918
- const argsStr = entry.function.args?.length ? entry.function.args.map((a) => `"${a}"`).join(", ") : "";
1919
- const executeClause = argsStr ? `EXECUTE FUNCTION ${entry.function.name}(${argsStr})` : `EXECUTE FUNCTION ${entry.function.name}()`;
1920
- this.writer.writeLine(
1921
- `await knex.raw(\`CREATE CONSTRAINT TRIGGER "${constraintName}" ${entry.when} ${eventsStr} ON "${table}"${deferrableClause} FOR EACH ${entry.forEach} ${executeClause}\`);`
1922
- );
1923
- this.writer.blankLine();
1924
- }
1925
- dropConstraintTrigger(table, constraintName) {
1926
- this.writer.writeLine(`await knex.raw('ALTER TABLE "${table}" DROP CONSTRAINT "${constraintName}"');`);
1927
- this.writer.blankLine();
1928
- }
1929
1790
  value(value2) {
1930
1791
  if (typeof value2 === "string") {
1931
1792
  return `'${value2}'`;