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

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,12 @@
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.8.0-next.1](https://github.com/smartive/graphql-magic/compare/v23.7.0...v23.8.0-next.1) (2026-03-04)
2
+
3
+ ### Features
4
+
5
+ * Exclude constraints and triggers ([12f5d85](https://github.com/smartive/graphql-magic/commit/12f5d85470bdf1107e023700d5a11012df69d098))
2
6
 
3
7
  ### Bug Fixes
4
8
 
5
- * constraint normalization for empty migration when schema in sync ([1a8e419](https://github.com/smartive/graphql-magic/commit/1a8e419f82899a9045f08ab96dbc9249e49dd773))
9
+ * constraint normalization for empty migration when schema in sync ([77a8283](https://github.com/smartive/graphql-magic/commit/77a828367abc78c07708556d7d751d780b785a9a))
10
+ * gen ([80bb12d](https://github.com/smartive/graphql-magic/commit/80bb12da6f7eb0aa231fb13c22019995f5811a6c))
11
+ * normalize identifier quoting in constraint comparison ([689136b](https://github.com/smartive/graphql-magic/commit/689136b1ef07874b24986c4596aae37acc92d74b))
12
+ * produce empty migration when schema is in sync ([e8e8e88](https://github.com/smartive/graphql-magic/commit/e8e8e8825a1d4ac833886f993a702c187364ea6f))
package/dist/bin/gqm.cjs CHANGED
@@ -1317,7 +1317,6 @@ var MigrationGenerator = class _MigrationGenerator {
1317
1317
  );
1318
1318
  this.updateFields(model, existingFields, up, down);
1319
1319
  if (model.constraints?.length) {
1320
- const existingCheckMap = this.existingCheckConstraints[model.name];
1321
1320
  const existingExcludeMap = this.existingExcludeConstraints[model.name];
1322
1321
  const existingTriggerMap = this.existingConstraintTriggers[model.name];
1323
1322
  for (let i = 0; i < model.constraints.length; i++) {
@@ -1326,23 +1325,31 @@ var MigrationGenerator = class _MigrationGenerator {
1326
1325
  const constraintName = this.getConstraintName(model, entry, i);
1327
1326
  if (entry.kind === "check") {
1328
1327
  validateCheckConstraint(model, entry);
1329
- const newExpression = entry.expression;
1330
- const existingExpression = existingCheckMap?.get(constraintName);
1331
- if (existingExpression === void 0) {
1328
+ const existingConstraint = this.findExistingConstraint(table, entry, constraintName);
1329
+ if (!existingConstraint) {
1332
1330
  up.push(() => {
1333
- this.addCheckConstraint(table, constraintName, newExpression, entry.deferrable);
1331
+ this.addCheckConstraint(table, constraintName, entry.expression, entry.deferrable);
1334
1332
  });
1335
1333
  down.push(() => {
1336
1334
  this.dropCheckConstraint(table, constraintName);
1337
1335
  });
1338
- } else if (this.normalizeCheckExpression(existingExpression) !== this.normalizeCheckExpression(newExpression)) {
1336
+ } else if (!await this.equalExpressions(
1337
+ table,
1338
+ existingConstraint.constraintName,
1339
+ existingConstraint.expression,
1340
+ entry.expression
1341
+ )) {
1339
1342
  up.push(() => {
1340
- this.dropCheckConstraint(table, constraintName);
1341
- this.addCheckConstraint(table, constraintName, newExpression, entry.deferrable);
1343
+ this.dropCheckConstraint(table, existingConstraint.constraintName);
1344
+ this.addCheckConstraint(table, constraintName, entry.expression, entry.deferrable);
1342
1345
  });
1343
1346
  down.push(() => {
1344
1347
  this.dropCheckConstraint(table, constraintName);
1345
- this.addCheckConstraint(table, constraintName, existingExpression);
1348
+ this.addCheckConstraint(
1349
+ table,
1350
+ existingConstraint.constraintName,
1351
+ existingConstraint.expression
1352
+ );
1346
1353
  });
1347
1354
  }
1348
1355
  } else if (entry.kind === "exclude") {
@@ -1814,64 +1821,144 @@ var MigrationGenerator = class _MigrationGenerator {
1814
1821
  }
1815
1822
  return result;
1816
1823
  }
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--;
1824
+ normalizeExcludeDef(def) {
1825
+ const s = def.replace(/\s+/g, " ").replace(/\s*\(\s*/g, "(").replace(/\s*\)\s*/g, ")").trim();
1826
+ return this.normalizeSqlIdentifiers(s);
1827
+ }
1828
+ normalizeTriggerDef(def) {
1829
+ const s = 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();
1830
+ return this.normalizeSqlIdentifiers(s);
1831
+ }
1832
+ normalizeCheckExpression(expr) {
1833
+ let normalized = expr.replace(/\s+/g, " ").trim();
1834
+ while (this.isWrappedByOuterParentheses(normalized)) {
1835
+ normalized = normalized.slice(1, -1).trim();
1836
+ }
1837
+ return normalized;
1838
+ }
1839
+ isWrappedByOuterParentheses(expr) {
1840
+ if (!expr.startsWith("(") || !expr.endsWith(")")) {
1841
+ return false;
1842
+ }
1843
+ let depth = 0;
1844
+ let inSingleQuote = false;
1845
+ for (let i = 0; i < expr.length; i++) {
1846
+ const char = expr[i];
1847
+ const next = expr[i + 1];
1848
+ if (char === "'") {
1849
+ if (inSingleQuote && next === "'") {
1850
+ i++;
1851
+ continue;
1826
1852
  }
1827
- if (depth === 0 && i < s.length - 1) {
1828
- match = false;
1829
- break;
1853
+ inSingleQuote = !inSingleQuote;
1854
+ continue;
1855
+ }
1856
+ if (inSingleQuote) {
1857
+ continue;
1858
+ }
1859
+ if (char === "(") {
1860
+ depth++;
1861
+ } else if (char === ")") {
1862
+ depth--;
1863
+ if (depth === 0 && i !== expr.length - 1) {
1864
+ return false;
1865
+ }
1866
+ if (depth < 0) {
1867
+ return false;
1830
1868
  }
1831
1869
  }
1832
- if (!match || depth !== 0) {
1833
- break;
1870
+ }
1871
+ return depth === 0;
1872
+ }
1873
+ findExistingConstraint(table, entry, preferredConstraintName) {
1874
+ const existingMap = this.existingCheckConstraints[table];
1875
+ if (!existingMap) {
1876
+ return null;
1877
+ }
1878
+ const preferredExpression = existingMap.get(preferredConstraintName);
1879
+ if (preferredExpression !== void 0) {
1880
+ return {
1881
+ constraintName: preferredConstraintName,
1882
+ expression: preferredExpression
1883
+ };
1884
+ }
1885
+ const normalizedNewExpression = this.normalizeCheckExpression(entry.expression);
1886
+ const constraintPrefix = `${table}_${entry.name}_${entry.kind}_`;
1887
+ for (const [constraintName, expression] of existingMap.entries()) {
1888
+ if (!constraintName.startsWith(constraintPrefix)) {
1889
+ continue;
1890
+ }
1891
+ if (this.normalizeCheckExpression(expression) !== normalizedNewExpression) {
1892
+ continue;
1834
1893
  }
1835
- s = s.slice(1, -1).trim();
1894
+ return { constraintName, expression };
1836
1895
  }
1837
- return s;
1896
+ return null;
1838
1897
  }
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--;
1898
+ async equalExpressions(table, constraintName, existingExpression, newExpression) {
1899
+ try {
1900
+ const [canonicalExisting, canonicalNew] = await Promise.all([
1901
+ this.canonicalizeCheckExpressionWithPostgres(table, existingExpression),
1902
+ this.canonicalizeCheckExpressionWithPostgres(table, newExpression)
1903
+ ]);
1904
+ return canonicalExisting === canonicalNew;
1905
+ } catch (error) {
1906
+ console.warn(
1907
+ `Failed to canonicalize check constraint "${constraintName}" on table "${table}". Treating it as changed.`,
1908
+ error
1909
+ );
1910
+ return false;
1911
+ }
1912
+ }
1913
+ async canonicalizeCheckExpressionWithPostgres(table, expression) {
1914
+ const sourceTableIdentifier = table.split(".").map((part) => this.quoteIdentifier(part)).join(".");
1915
+ const uniqueSuffix = `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
1916
+ const tableSlug = table.toLowerCase().replace(/[^a-z0-9_]/g, "_");
1917
+ const tempTableName = `gqm_tmp_check_${tableSlug}_${uniqueSuffix}`;
1918
+ const tempTableIdentifier = this.quoteIdentifier(tempTableName);
1919
+ const constraintName = `gqm_tmp_check_${uniqueSuffix}`;
1920
+ const constraintIdentifier = this.quoteIdentifier(constraintName);
1921
+ const trx = await this.knex.transaction();
1922
+ try {
1923
+ await trx.raw(`CREATE TEMP TABLE ${tempTableIdentifier} (LIKE ${sourceTableIdentifier}) ON COMMIT DROP`);
1924
+ await trx.raw(`ALTER TABLE ${tempTableIdentifier} ADD CONSTRAINT ${constraintIdentifier} CHECK (${expression})`);
1925
+ const result = await trx.raw(
1926
+ `SELECT pg_get_constraintdef(c.oid, true) AS constraint_definition
1927
+ FROM pg_constraint c
1928
+ JOIN pg_class t
1929
+ ON t.oid = c.conrelid
1930
+ WHERE t.relname = ?
1931
+ AND c.conname = ?
1932
+ ORDER BY c.oid DESC
1933
+ LIMIT 1`,
1934
+ [tempTableName, constraintName]
1935
+ );
1936
+ const rows = "rows" in result && Array.isArray(result.rows) ? result.rows : [];
1937
+ const definition = rows[0]?.constraint_definition;
1938
+ if (!definition) {
1939
+ throw new Error(`Could not read canonical check definition for expression: ${expression}`);
1849
1940
  }
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;
1941
+ return this.normalizeCheckExpression(this.extractCheckExpressionFromDefinition(definition));
1942
+ } finally {
1943
+ try {
1944
+ await trx.rollback();
1945
+ } catch {
1854
1946
  }
1855
1947
  }
1856
- parts.push(s.slice(start).trim());
1857
- return parts.filter(Boolean);
1858
1948
  }
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);
1949
+ extractCheckExpressionFromDefinition(definition) {
1950
+ const trimmed = definition.trim();
1951
+ const match = trimmed.match(/^CHECK\s*\(([\s\S]*)\)$/i);
1952
+ if (!match) {
1953
+ return trimmed;
1954
+ }
1955
+ return match[1];
1868
1956
  }
1869
- normalizeExcludeDef(def) {
1870
- const s = def.replace(/\s+/g, " ").replace(/\s*\(\s*/g, "(").replace(/\s*\)\s*/g, ")").trim();
1871
- return this.normalizeSqlIdentifiers(s);
1957
+ quoteIdentifier(identifier) {
1958
+ return `"${identifier.replace(/"/g, '""')}"`;
1872
1959
  }
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();
1960
+ quoteQualifiedIdentifier(identifier) {
1961
+ return identifier.split(".").map((part) => this.quoteIdentifier(part)).join(".");
1875
1962
  }
1876
1963
  /** Escape expression for embedding inside a template literal in generated code */
1877
1964
  escapeExpressionForRaw(expr) {
@@ -3332,7 +3332,6 @@ var MigrationGenerator = class _MigrationGenerator {
3332
3332
  );
3333
3333
  this.updateFields(model, existingFields, up, down);
3334
3334
  if (model.constraints?.length) {
3335
- const existingCheckMap = this.existingCheckConstraints[model.name];
3336
3335
  const existingExcludeMap = this.existingExcludeConstraints[model.name];
3337
3336
  const existingTriggerMap = this.existingConstraintTriggers[model.name];
3338
3337
  for (let i = 0; i < model.constraints.length; i++) {
@@ -3341,23 +3340,31 @@ var MigrationGenerator = class _MigrationGenerator {
3341
3340
  const constraintName = this.getConstraintName(model, entry, i);
3342
3341
  if (entry.kind === "check") {
3343
3342
  validateCheckConstraint(model, entry);
3344
- const newExpression = entry.expression;
3345
- const existingExpression = existingCheckMap?.get(constraintName);
3346
- if (existingExpression === void 0) {
3343
+ const existingConstraint = this.findExistingConstraint(table, entry, constraintName);
3344
+ if (!existingConstraint) {
3347
3345
  up.push(() => {
3348
- this.addCheckConstraint(table, constraintName, newExpression, entry.deferrable);
3346
+ this.addCheckConstraint(table, constraintName, entry.expression, entry.deferrable);
3349
3347
  });
3350
3348
  down.push(() => {
3351
3349
  this.dropCheckConstraint(table, constraintName);
3352
3350
  });
3353
- } else if (this.normalizeCheckExpression(existingExpression) !== this.normalizeCheckExpression(newExpression)) {
3351
+ } else if (!await this.equalExpressions(
3352
+ table,
3353
+ existingConstraint.constraintName,
3354
+ existingConstraint.expression,
3355
+ entry.expression
3356
+ )) {
3354
3357
  up.push(() => {
3355
- this.dropCheckConstraint(table, constraintName);
3356
- this.addCheckConstraint(table, constraintName, newExpression, entry.deferrable);
3358
+ this.dropCheckConstraint(table, existingConstraint.constraintName);
3359
+ this.addCheckConstraint(table, constraintName, entry.expression, entry.deferrable);
3357
3360
  });
3358
3361
  down.push(() => {
3359
3362
  this.dropCheckConstraint(table, constraintName);
3360
- this.addCheckConstraint(table, constraintName, existingExpression);
3363
+ this.addCheckConstraint(
3364
+ table,
3365
+ existingConstraint.constraintName,
3366
+ existingConstraint.expression
3367
+ );
3361
3368
  });
3362
3369
  }
3363
3370
  } else if (entry.kind === "exclude") {
@@ -3829,64 +3836,144 @@ var MigrationGenerator = class _MigrationGenerator {
3829
3836
  }
3830
3837
  return result;
3831
3838
  }
3832
- stripOuterParens(s) {
3833
- while (s.length >= 2 && s.startsWith("(") && s.endsWith(")")) {
3834
- let depth = 0;
3835
- let match = true;
3836
- for (let i = 0; i < s.length; i++) {
3837
- if (s[i] === "(") {
3838
- depth++;
3839
- } else if (s[i] === ")") {
3840
- depth--;
3839
+ normalizeExcludeDef(def) {
3840
+ const s = def.replace(/\s+/g, " ").replace(/\s*\(\s*/g, "(").replace(/\s*\)\s*/g, ")").trim();
3841
+ return this.normalizeSqlIdentifiers(s);
3842
+ }
3843
+ normalizeTriggerDef(def) {
3844
+ const s = 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();
3845
+ return this.normalizeSqlIdentifiers(s);
3846
+ }
3847
+ normalizeCheckExpression(expr) {
3848
+ let normalized = expr.replace(/\s+/g, " ").trim();
3849
+ while (this.isWrappedByOuterParentheses(normalized)) {
3850
+ normalized = normalized.slice(1, -1).trim();
3851
+ }
3852
+ return normalized;
3853
+ }
3854
+ isWrappedByOuterParentheses(expr) {
3855
+ if (!expr.startsWith("(") || !expr.endsWith(")")) {
3856
+ return false;
3857
+ }
3858
+ let depth = 0;
3859
+ let inSingleQuote = false;
3860
+ for (let i = 0; i < expr.length; i++) {
3861
+ const char = expr[i];
3862
+ const next = expr[i + 1];
3863
+ if (char === "'") {
3864
+ if (inSingleQuote && next === "'") {
3865
+ i++;
3866
+ continue;
3841
3867
  }
3842
- if (depth === 0 && i < s.length - 1) {
3843
- match = false;
3844
- break;
3868
+ inSingleQuote = !inSingleQuote;
3869
+ continue;
3870
+ }
3871
+ if (inSingleQuote) {
3872
+ continue;
3873
+ }
3874
+ if (char === "(") {
3875
+ depth++;
3876
+ } else if (char === ")") {
3877
+ depth--;
3878
+ if (depth === 0 && i !== expr.length - 1) {
3879
+ return false;
3880
+ }
3881
+ if (depth < 0) {
3882
+ return false;
3845
3883
  }
3846
3884
  }
3847
- if (!match || depth !== 0) {
3848
- break;
3885
+ }
3886
+ return depth === 0;
3887
+ }
3888
+ findExistingConstraint(table, entry, preferredConstraintName) {
3889
+ const existingMap = this.existingCheckConstraints[table];
3890
+ if (!existingMap) {
3891
+ return null;
3892
+ }
3893
+ const preferredExpression = existingMap.get(preferredConstraintName);
3894
+ if (preferredExpression !== void 0) {
3895
+ return {
3896
+ constraintName: preferredConstraintName,
3897
+ expression: preferredExpression
3898
+ };
3899
+ }
3900
+ const normalizedNewExpression = this.normalizeCheckExpression(entry.expression);
3901
+ const constraintPrefix = `${table}_${entry.name}_${entry.kind}_`;
3902
+ for (const [constraintName, expression] of existingMap.entries()) {
3903
+ if (!constraintName.startsWith(constraintPrefix)) {
3904
+ continue;
3905
+ }
3906
+ if (this.normalizeCheckExpression(expression) !== normalizedNewExpression) {
3907
+ continue;
3849
3908
  }
3850
- s = s.slice(1, -1).trim();
3909
+ return { constraintName, expression };
3851
3910
  }
3852
- return s;
3911
+ return null;
3853
3912
  }
3854
- splitAtTopLevel(s, sep) {
3855
- const parts = [];
3856
- let depth = 0;
3857
- let start = 0;
3858
- const sepLen = sep.length;
3859
- for (let i = 0; i <= s.length - sepLen; i++) {
3860
- if (s[i] === "(") {
3861
- depth++;
3862
- } else if (s[i] === ")") {
3863
- depth--;
3913
+ async equalExpressions(table, constraintName, existingExpression, newExpression) {
3914
+ try {
3915
+ const [canonicalExisting, canonicalNew] = await Promise.all([
3916
+ this.canonicalizeCheckExpressionWithPostgres(table, existingExpression),
3917
+ this.canonicalizeCheckExpressionWithPostgres(table, newExpression)
3918
+ ]);
3919
+ return canonicalExisting === canonicalNew;
3920
+ } catch (error) {
3921
+ console.warn(
3922
+ `Failed to canonicalize check constraint "${constraintName}" on table "${table}". Treating it as changed.`,
3923
+ error
3924
+ );
3925
+ return false;
3926
+ }
3927
+ }
3928
+ async canonicalizeCheckExpressionWithPostgres(table, expression) {
3929
+ const sourceTableIdentifier = table.split(".").map((part) => this.quoteIdentifier(part)).join(".");
3930
+ const uniqueSuffix = `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
3931
+ const tableSlug = table.toLowerCase().replace(/[^a-z0-9_]/g, "_");
3932
+ const tempTableName = `gqm_tmp_check_${tableSlug}_${uniqueSuffix}`;
3933
+ const tempTableIdentifier = this.quoteIdentifier(tempTableName);
3934
+ const constraintName = `gqm_tmp_check_${uniqueSuffix}`;
3935
+ const constraintIdentifier = this.quoteIdentifier(constraintName);
3936
+ const trx = await this.knex.transaction();
3937
+ try {
3938
+ await trx.raw(`CREATE TEMP TABLE ${tempTableIdentifier} (LIKE ${sourceTableIdentifier}) ON COMMIT DROP`);
3939
+ await trx.raw(`ALTER TABLE ${tempTableIdentifier} ADD CONSTRAINT ${constraintIdentifier} CHECK (${expression})`);
3940
+ const result = await trx.raw(
3941
+ `SELECT pg_get_constraintdef(c.oid, true) AS constraint_definition
3942
+ FROM pg_constraint c
3943
+ JOIN pg_class t
3944
+ ON t.oid = c.conrelid
3945
+ WHERE t.relname = ?
3946
+ AND c.conname = ?
3947
+ ORDER BY c.oid DESC
3948
+ LIMIT 1`,
3949
+ [tempTableName, constraintName]
3950
+ );
3951
+ const rows = "rows" in result && Array.isArray(result.rows) ? result.rows : [];
3952
+ const definition = rows[0]?.constraint_definition;
3953
+ if (!definition) {
3954
+ throw new Error(`Could not read canonical check definition for expression: ${expression}`);
3864
3955
  }
3865
- if (depth === 0 && s.slice(i, i + sepLen).toLowerCase() === sep.toLowerCase()) {
3866
- parts.push(s.slice(start, i).trim());
3867
- start = i + sepLen;
3868
- i += sepLen - 1;
3956
+ return this.normalizeCheckExpression(this.extractCheckExpressionFromDefinition(definition));
3957
+ } finally {
3958
+ try {
3959
+ await trx.rollback();
3960
+ } catch {
3869
3961
  }
3870
3962
  }
3871
- parts.push(s.slice(start).trim());
3872
- return parts.filter(Boolean);
3873
3963
  }
3874
- normalizeCheckExpression(expr) {
3875
- let s = expr.replace(/\s+/g, " ").trim();
3876
- const normalizeParts = (str, separator) => this.splitAtTopLevel(str, separator).map((p) => this.stripOuterParens(p)).join(separator);
3877
- s = this.stripOuterParens(s);
3878
- s = normalizeParts(s, " OR ");
3879
- s = this.stripOuterParens(s);
3880
- s = normalizeParts(s, " AND ");
3881
- s = s.replace(/\s*\(\s*/g, "(").replace(/\s*\)\s*/g, ")").replace(/\s+AND\s+/gi, " AND ").replace(/\s+OR\s+/gi, " OR ").trim();
3882
- return this.normalizeSqlIdentifiers(s);
3964
+ extractCheckExpressionFromDefinition(definition) {
3965
+ const trimmed = definition.trim();
3966
+ const match = trimmed.match(/^CHECK\s*\(([\s\S]*)\)$/i);
3967
+ if (!match) {
3968
+ return trimmed;
3969
+ }
3970
+ return match[1];
3883
3971
  }
3884
- normalizeExcludeDef(def) {
3885
- const s = def.replace(/\s+/g, " ").replace(/\s*\(\s*/g, "(").replace(/\s*\)\s*/g, ")").trim();
3886
- return this.normalizeSqlIdentifiers(s);
3972
+ quoteIdentifier(identifier) {
3973
+ return `"${identifier.replace(/"/g, '""')}"`;
3887
3974
  }
3888
- normalizeTriggerDef(def) {
3889
- 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();
3975
+ quoteQualifiedIdentifier(identifier) {
3976
+ return identifier.split(".").map((part) => this.quoteIdentifier(part)).join(".");
3890
3977
  }
3891
3978
  /** Escape expression for embedding inside a template literal in generated code */
3892
3979
  escapeExpressionForRaw(expr) {
@@ -38,11 +38,16 @@ export declare class MigrationGenerator {
38
38
  private static readonly SQL_KEYWORDS;
39
39
  private static readonly LITERAL_PLACEHOLDER;
40
40
  private normalizeSqlIdentifiers;
41
- private stripOuterParens;
42
- private splitAtTopLevel;
43
- private normalizeCheckExpression;
44
41
  private normalizeExcludeDef;
45
42
  private normalizeTriggerDef;
43
+ private normalizeCheckExpression;
44
+ private isWrappedByOuterParentheses;
45
+ private findExistingConstraint;
46
+ private equalExpressions;
47
+ private canonicalizeCheckExpressionWithPostgres;
48
+ private extractCheckExpressionFromDefinition;
49
+ private quoteIdentifier;
50
+ private quoteQualifiedIdentifier;
46
51
  /** Escape expression for embedding inside a template literal in generated code */
47
52
  private escapeExpressionForRaw;
48
53
  private addCheckConstraint;