@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.
@@ -198,7 +198,6 @@ __export(index_exports, {
198
198
  updateEntity: () => updateEntity,
199
199
  updateFunctions: () => updateFunctions,
200
200
  validateCheckConstraint: () => validateCheckConstraint,
201
- validateExcludeConstraint: () => validateExcludeConstraint,
202
201
  value: () => value
203
202
  });
204
203
  module.exports = __toCommonJS(index_exports);
@@ -655,8 +654,6 @@ var EntityModel = class extends Model {
655
654
  for (const constraint of this.constraints) {
656
655
  if (constraint.kind === "check") {
657
656
  validateCheckConstraint(this, constraint);
658
- } else if (constraint.kind === "exclude") {
659
- validateExcludeConstraint(this, constraint);
660
657
  }
661
658
  }
662
659
  }
@@ -955,19 +952,6 @@ var validateCheckConstraint = (model, constraint) => {
955
952
  }
956
953
  }
957
954
  };
958
- var validateExcludeConstraint = (model, constraint) => {
959
- const validColumnNames = new Set(model.fields.map((f) => getColumnName(f)));
960
- for (const el of constraint.elements) {
961
- if ("column" in el) {
962
- if (!validColumnNames.has(el.column)) {
963
- const validList = [...validColumnNames].sort().join(", ");
964
- throw new Error(
965
- `Exclude constraint "${constraint.name}" references column "${el.column}" which does not exist on model ${model.name}. Valid columns: ${validList}`
966
- );
967
- }
968
- }
969
- }
970
- };
971
955
 
972
956
  // src/client/queries.ts
973
957
  var fieldIsSearchable = (model, fieldName) => {
@@ -1182,7 +1166,6 @@ var generateFunctionsFromDatabase = async (knex) => {
1182
1166
  JOIN pg_namespace n ON p.pronamespace = n.oid
1183
1167
  WHERE n.nspname = 'public'
1184
1168
  AND NOT EXISTS (SELECT 1 FROM pg_aggregate a WHERE a.aggfnoid = p.oid)
1185
- AND NOT EXISTS (SELECT 1 FROM pg_depend d WHERE d.objid = p.oid AND d.deptype = 'e')
1186
1169
  ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
1187
1170
  `);
1188
1171
  const aggregateFunctions = await knex.raw(`
@@ -1197,7 +1180,6 @@ var generateFunctionsFromDatabase = async (knex) => {
1197
1180
  JOIN pg_aggregate a ON p.oid = a.aggfnoid
1198
1181
  JOIN pg_namespace n ON p.pronamespace = n.oid
1199
1182
  WHERE n.nspname = 'public'
1200
- AND NOT EXISTS (SELECT 1 FROM pg_depend d WHERE d.objid = p.oid AND d.deptype = 'e')
1201
1183
  ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
1202
1184
  `);
1203
1185
  const functions = [];
@@ -2942,7 +2924,6 @@ var getDatabaseFunctions = async (knex) => {
2942
2924
  JOIN pg_namespace n ON p.pronamespace = n.oid
2943
2925
  WHERE n.nspname = 'public'
2944
2926
  AND NOT EXISTS (SELECT 1 FROM pg_aggregate a WHERE a.aggfnoid = p.oid)
2945
- AND NOT EXISTS (SELECT 1 FROM pg_depend d WHERE d.objid = p.oid AND d.deptype = 'e')
2946
2927
  ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
2947
2928
  `);
2948
2929
  const aggregateFunctions = await knex.raw(`
@@ -2958,7 +2939,6 @@ var getDatabaseFunctions = async (knex) => {
2958
2939
  JOIN pg_aggregate a ON p.oid = a.aggfnoid
2959
2940
  JOIN pg_namespace n ON p.pronamespace = n.oid
2960
2941
  WHERE n.nspname = 'public'
2961
- AND NOT EXISTS (SELECT 1 FROM pg_depend d WHERE d.objid = p.oid AND d.deptype = 'e')
2962
2942
  ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
2963
2943
  `);
2964
2944
  const result = [];
@@ -3102,7 +3082,7 @@ Summary: ${updatedCount} updated, ${skippedCount} skipped`);
3102
3082
  };
3103
3083
 
3104
3084
  // src/migrations/generate.ts
3105
- var MigrationGenerator = class _MigrationGenerator {
3085
+ var MigrationGenerator = class {
3106
3086
  constructor(knex, models, parsedFunctions) {
3107
3087
  this.models = models;
3108
3088
  this.parsedFunctions = parsedFunctions;
@@ -3118,10 +3098,6 @@ var MigrationGenerator = class _MigrationGenerator {
3118
3098
  columns = {};
3119
3099
  /** table name -> constraint name -> check clause expression */
3120
3100
  existingCheckConstraints = {};
3121
- /** table name -> constraint name -> { normalized, raw } */
3122
- existingExcludeConstraints = {};
3123
- /** table name -> constraint name -> { normalized, raw } */
3124
- existingConstraintTriggers = {};
3125
3101
  uuidUsed;
3126
3102
  nowUsed;
3127
3103
  needsMigration = false;
@@ -3149,56 +3125,8 @@ var MigrationGenerator = class _MigrationGenerator {
3149
3125
  }
3150
3126
  this.existingCheckConstraints[tableName].set(row.constraint_name, row.check_clause);
3151
3127
  }
3152
- const excludeResult = await schema.knex.raw(
3153
- `SELECT c.conrelid::regclass::text as table_name, c.conname as constraint_name, pg_get_constraintdef(c.oid) as constraint_def
3154
- FROM pg_constraint c
3155
- JOIN pg_namespace n ON c.connamespace = n.oid
3156
- WHERE n.nspname = 'public' AND c.contype = 'x'`
3157
- );
3158
- const excludeRows = "rows" in excludeResult && Array.isArray(excludeResult.rows) ? excludeResult.rows : [];
3159
- for (const row of excludeRows) {
3160
- const tableName = row.table_name.split(".").pop()?.replace(/^"|"$/g, "") ?? row.table_name;
3161
- if (!this.existingExcludeConstraints[tableName]) {
3162
- this.existingExcludeConstraints[tableName] = /* @__PURE__ */ new Map();
3163
- }
3164
- this.existingExcludeConstraints[tableName].set(row.constraint_name, {
3165
- normalized: this.normalizeExcludeDef(row.constraint_def),
3166
- raw: row.constraint_def
3167
- });
3168
- }
3169
- const triggerResult = await schema.knex.raw(
3170
- `SELECT c.conrelid::regclass::text as table_name, c.conname as constraint_name, pg_get_triggerdef(t.oid) as trigger_def
3171
- FROM pg_constraint c
3172
- JOIN pg_trigger t ON t.tgconstraint = c.oid
3173
- JOIN pg_namespace n ON c.connamespace = n.oid
3174
- WHERE n.nspname = 'public' AND c.contype = 't'`
3175
- );
3176
- const triggerRows = "rows" in triggerResult && Array.isArray(triggerResult.rows) ? triggerResult.rows : [];
3177
- for (const row of triggerRows) {
3178
- const tableName = row.table_name.split(".").pop()?.replace(/^"|"$/g, "") ?? row.table_name;
3179
- if (!this.existingConstraintTriggers[tableName]) {
3180
- this.existingConstraintTriggers[tableName] = /* @__PURE__ */ new Map();
3181
- }
3182
- this.existingConstraintTriggers[tableName].set(row.constraint_name, {
3183
- normalized: this.normalizeTriggerDef(row.trigger_def),
3184
- raw: row.trigger_def
3185
- });
3186
- }
3187
3128
  const up = [];
3188
3129
  const down = [];
3189
- const wantsBtreeGist = models.entities.some(
3190
- (model) => model.constraints?.some((c) => c.kind === "exclude" && c.elements.some((el) => "column" in el && el.operator === "="))
3191
- );
3192
- if (wantsBtreeGist) {
3193
- const extResult = await schema.knex("pg_extension").where("extname", "btree_gist").select("oid").first();
3194
- const btreeGistInstalled = !!extResult;
3195
- if (!btreeGistInstalled) {
3196
- up.unshift(() => {
3197
- this.writer.writeLine(`await knex.raw('CREATE EXTENSION IF NOT EXISTS btree_gist');`);
3198
- this.writer.blankLine();
3199
- });
3200
- }
3201
- }
3202
3130
  this.createEnums(
3203
3131
  this.models.enums.filter((enm2) => !enums.includes((0, import_lowerFirst.default)(enm2.name))),
3204
3132
  up,
@@ -3277,22 +3205,10 @@ var MigrationGenerator = class _MigrationGenerator {
3277
3205
  if (entry.kind === "check") {
3278
3206
  validateCheckConstraint(model, entry);
3279
3207
  const table = model.name;
3280
- const constraintName = this.getConstraintName(model, entry, i);
3208
+ const constraintName = this.getCheckConstraintName(model, entry, i);
3209
+ const expression = entry.expression;
3281
3210
  up.push(() => {
3282
- this.addCheckConstraint(table, constraintName, entry.expression, entry.deferrable);
3283
- });
3284
- } else if (entry.kind === "exclude") {
3285
- validateExcludeConstraint(model, entry);
3286
- const table = model.name;
3287
- const constraintName = this.getConstraintName(model, entry, i);
3288
- up.push(() => {
3289
- this.addExcludeConstraint(table, constraintName, entry);
3290
- });
3291
- } else if (entry.kind === "constraint_trigger") {
3292
- const table = model.name;
3293
- const constraintName = this.getConstraintName(model, entry, i);
3294
- up.push(() => {
3295
- this.addConstraintTrigger(table, constraintName, entry);
3211
+ this.addCheckConstraint(table, constraintName, expression);
3296
3212
  });
3297
3213
  }
3298
3214
  }
@@ -3332,81 +3248,36 @@ var MigrationGenerator = class _MigrationGenerator {
3332
3248
  );
3333
3249
  this.updateFields(model, existingFields, up, down);
3334
3250
  if (model.constraints?.length) {
3335
- const existingCheckMap = this.existingCheckConstraints[model.name];
3336
- const existingExcludeMap = this.existingExcludeConstraints[model.name];
3337
- const existingTriggerMap = this.existingConstraintTriggers[model.name];
3338
3251
  for (let i = 0; i < model.constraints.length; i++) {
3339
3252
  const entry = model.constraints[i];
3253
+ if (entry.kind !== "check") {
3254
+ continue;
3255
+ }
3256
+ validateCheckConstraint(model, entry);
3340
3257
  const table = model.name;
3341
- const constraintName = this.getConstraintName(model, entry, i);
3342
- if (entry.kind === "check") {
3343
- validateCheckConstraint(model, entry);
3344
- const newExpression = entry.expression;
3345
- const existingExpression = existingCheckMap?.get(constraintName);
3346
- if (existingExpression === void 0) {
3347
- up.push(() => {
3348
- this.addCheckConstraint(table, constraintName, newExpression, entry.deferrable);
3349
- });
3350
- down.push(() => {
3351
- this.dropCheckConstraint(table, constraintName);
3352
- });
3353
- } else if (this.normalizeCheckExpression(existingExpression) !== this.normalizeCheckExpression(newExpression)) {
3354
- up.push(() => {
3355
- this.dropCheckConstraint(table, constraintName);
3356
- this.addCheckConstraint(table, constraintName, newExpression, entry.deferrable);
3357
- });
3358
- down.push(() => {
3359
- this.dropCheckConstraint(table, constraintName);
3360
- this.addCheckConstraint(table, constraintName, existingExpression);
3361
- });
3362
- }
3363
- } else if (entry.kind === "exclude") {
3364
- validateExcludeConstraint(model, entry);
3365
- const newDef = this.normalizeExcludeDef(this.buildExcludeDef(entry));
3366
- const existing = existingExcludeMap?.get(constraintName);
3367
- if (existing === void 0) {
3368
- up.push(() => {
3369
- this.addExcludeConstraint(table, constraintName, entry);
3370
- });
3371
- down.push(() => {
3372
- this.dropExcludeConstraint(table, constraintName);
3373
- });
3374
- } else if (existing.normalized !== newDef) {
3375
- up.push(() => {
3376
- this.dropExcludeConstraint(table, constraintName);
3377
- this.addExcludeConstraint(table, constraintName, entry);
3378
- });
3379
- down.push(() => {
3380
- this.dropExcludeConstraint(table, constraintName);
3381
- const escaped = this.escapeExpressionForRaw(existing.raw);
3382
- this.writer.writeLine(
3383
- `await knex.raw(\`ALTER TABLE "${table}" ADD CONSTRAINT "${constraintName}" ${escaped}\`);`
3384
- );
3385
- this.writer.blankLine();
3386
- });
3387
- }
3388
- } else if (entry.kind === "constraint_trigger") {
3389
- const newDef = this.normalizeTriggerDef(this.buildConstraintTriggerDef(table, constraintName, entry));
3390
- const existing = existingTriggerMap?.get(constraintName);
3391
- if (existing === void 0) {
3392
- up.push(() => {
3393
- this.addConstraintTrigger(table, constraintName, entry);
3394
- });
3395
- down.push(() => {
3396
- this.dropConstraintTrigger(table, constraintName);
3397
- });
3398
- } else if (existing.normalized !== newDef) {
3399
- up.push(() => {
3400
- this.dropConstraintTrigger(table, constraintName);
3401
- this.addConstraintTrigger(table, constraintName, entry);
3402
- });
3403
- down.push(() => {
3404
- this.dropConstraintTrigger(table, constraintName);
3405
- const escaped = existing.raw.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$");
3406
- this.writer.writeLine(`await knex.raw(\`${escaped}\`);`);
3407
- this.writer.blankLine();
3408
- });
3409
- }
3258
+ const constraintName = this.getCheckConstraintName(model, entry, i);
3259
+ const existingConstraint = this.findExistingConstraint(table, entry, constraintName);
3260
+ if (!existingConstraint) {
3261
+ up.push(() => {
3262
+ this.addCheckConstraint(table, constraintName, entry.expression);
3263
+ });
3264
+ down.push(() => {
3265
+ this.dropCheckConstraint(table, constraintName);
3266
+ });
3267
+ } else if (!await this.equalExpressions(
3268
+ table,
3269
+ existingConstraint.constraintName,
3270
+ existingConstraint.expression,
3271
+ entry.expression
3272
+ )) {
3273
+ up.push(() => {
3274
+ this.dropCheckConstraint(table, existingConstraint.constraintName);
3275
+ this.addCheckConstraint(table, constraintName, entry.expression);
3276
+ });
3277
+ down.push(() => {
3278
+ this.dropCheckConstraint(table, constraintName);
3279
+ this.addCheckConstraint(table, existingConstraint.constraintName, existingConstraint.expression);
3280
+ });
3410
3281
  }
3411
3282
  }
3412
3283
  }
@@ -3781,122 +3652,148 @@ var MigrationGenerator = class _MigrationGenerator {
3781
3652
  renameColumn(from, to) {
3782
3653
  this.writer.writeLine(`table.renameColumn('${from}', '${to}');`);
3783
3654
  }
3784
- getConstraintName(model, entry, index) {
3655
+ getCheckConstraintName(model, entry, index) {
3785
3656
  return `${model.name}_${entry.name}_${entry.kind}_${index}`;
3786
3657
  }
3787
- static SQL_KEYWORDS = /* @__PURE__ */ new Set([
3788
- "and",
3789
- "or",
3790
- "not",
3791
- "in",
3792
- "is",
3793
- "null",
3794
- "true",
3795
- "false",
3796
- "between",
3797
- "like",
3798
- "exists",
3799
- "all",
3800
- "any",
3801
- "asc",
3802
- "desc",
3803
- "with",
3804
- "using",
3805
- "as",
3806
- "on",
3807
- "infinity",
3808
- "extract",
3809
- "current_date",
3810
- "current_timestamp"
3811
- ]);
3812
- static LITERAL_PLACEHOLDER = "\uE000";
3813
- normalizeSqlIdentifiers(s) {
3814
- const literals = [];
3815
- let result = s.replace(/'([^']|'')*'/g, (lit) => {
3816
- literals.push(lit);
3817
- return `${_MigrationGenerator.LITERAL_PLACEHOLDER}${literals.length - 1}${_MigrationGenerator.LITERAL_PLACEHOLDER}`;
3818
- });
3819
- result = result.replace(/"([^"]*)"/g, (_, ident) => `"${ident.toLowerCase()}"`);
3820
- result = result.replace(
3821
- /\b([a-zA-Z_][a-zA-Z0-9_]*)\b(?!\s*\()/g,
3822
- (match) => _MigrationGenerator.SQL_KEYWORDS.has(match.toLowerCase()) ? match : `"${match.toLowerCase()}"`
3823
- );
3824
- for (let i = 0; i < literals.length; i++) {
3825
- result = result.replace(
3826
- new RegExp(`${_MigrationGenerator.LITERAL_PLACEHOLDER}${i}${_MigrationGenerator.LITERAL_PLACEHOLDER}`, "g"),
3827
- literals[i]
3828
- );
3658
+ normalizeCheckExpression(expr) {
3659
+ let normalized = expr.replace(/\s+/g, " ").trim();
3660
+ while (this.isWrappedByOuterParentheses(normalized)) {
3661
+ normalized = normalized.slice(1, -1).trim();
3829
3662
  }
3830
- return result;
3831
- }
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--;
3663
+ return normalized;
3664
+ }
3665
+ isWrappedByOuterParentheses(expr) {
3666
+ if (!expr.startsWith("(") || !expr.endsWith(")")) {
3667
+ return false;
3668
+ }
3669
+ let depth = 0;
3670
+ let inSingleQuote = false;
3671
+ for (let i = 0; i < expr.length; i++) {
3672
+ const char = expr[i];
3673
+ const next = expr[i + 1];
3674
+ if (char === "'") {
3675
+ if (inSingleQuote && next === "'") {
3676
+ i++;
3677
+ continue;
3841
3678
  }
3842
- if (depth === 0 && i < s.length - 1) {
3843
- match = false;
3844
- break;
3679
+ inSingleQuote = !inSingleQuote;
3680
+ continue;
3681
+ }
3682
+ if (inSingleQuote) {
3683
+ continue;
3684
+ }
3685
+ if (char === "(") {
3686
+ depth++;
3687
+ } else if (char === ")") {
3688
+ depth--;
3689
+ if (depth === 0 && i !== expr.length - 1) {
3690
+ return false;
3691
+ }
3692
+ if (depth < 0) {
3693
+ return false;
3845
3694
  }
3846
3695
  }
3847
- if (!match || depth !== 0) {
3848
- break;
3696
+ }
3697
+ return depth === 0;
3698
+ }
3699
+ findExistingConstraint(table, entry, preferredConstraintName) {
3700
+ const existingMap = this.existingCheckConstraints[table];
3701
+ if (!existingMap) {
3702
+ return null;
3703
+ }
3704
+ const preferredExpression = existingMap.get(preferredConstraintName);
3705
+ if (preferredExpression !== void 0) {
3706
+ return {
3707
+ constraintName: preferredConstraintName,
3708
+ expression: preferredExpression
3709
+ };
3710
+ }
3711
+ const normalizedNewExpression = this.normalizeCheckExpression(entry.expression);
3712
+ const constraintPrefix = `${table}_${entry.name}_${entry.kind}_`;
3713
+ for (const [constraintName, expression] of existingMap.entries()) {
3714
+ if (!constraintName.startsWith(constraintPrefix)) {
3715
+ continue;
3849
3716
  }
3850
- s = s.slice(1, -1).trim();
3717
+ if (this.normalizeCheckExpression(expression) !== normalizedNewExpression) {
3718
+ continue;
3719
+ }
3720
+ return { constraintName, expression };
3851
3721
  }
3852
- return s;
3722
+ return null;
3853
3723
  }
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--;
3724
+ async equalExpressions(table, constraintName, existingExpression, newExpression) {
3725
+ try {
3726
+ const [canonicalExisting, canonicalNew] = await Promise.all([
3727
+ this.canonicalizeCheckExpressionWithPostgres(table, existingExpression),
3728
+ this.canonicalizeCheckExpressionWithPostgres(table, newExpression)
3729
+ ]);
3730
+ return canonicalExisting === canonicalNew;
3731
+ } catch (error) {
3732
+ console.warn(
3733
+ `Failed to canonicalize check constraint "${constraintName}" on table "${table}". Treating it as changed.`,
3734
+ error
3735
+ );
3736
+ return false;
3737
+ }
3738
+ }
3739
+ async canonicalizeCheckExpressionWithPostgres(table, expression) {
3740
+ const sourceTableIdentifier = table.split(".").map((part) => this.quoteIdentifier(part)).join(".");
3741
+ const uniqueSuffix = `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
3742
+ const tableSlug = table.toLowerCase().replace(/[^a-z0-9_]/g, "_");
3743
+ const tempTableName = `gqm_tmp_check_${tableSlug}_${uniqueSuffix}`;
3744
+ const tempTableIdentifier = this.quoteIdentifier(tempTableName);
3745
+ const constraintName = `gqm_tmp_check_${uniqueSuffix}`;
3746
+ const constraintIdentifier = this.quoteIdentifier(constraintName);
3747
+ const trx = await this.knex.transaction();
3748
+ try {
3749
+ await trx.raw(`CREATE TEMP TABLE ${tempTableIdentifier} (LIKE ${sourceTableIdentifier}) ON COMMIT DROP`);
3750
+ await trx.raw(`ALTER TABLE ${tempTableIdentifier} ADD CONSTRAINT ${constraintIdentifier} CHECK (${expression})`);
3751
+ const result = await trx.raw(
3752
+ `SELECT pg_get_constraintdef(c.oid, true) AS constraint_definition
3753
+ FROM pg_constraint c
3754
+ JOIN pg_class t
3755
+ ON t.oid = c.conrelid
3756
+ WHERE t.relname = ?
3757
+ AND c.conname = ?
3758
+ ORDER BY c.oid DESC
3759
+ LIMIT 1`,
3760
+ [tempTableName, constraintName]
3761
+ );
3762
+ const rows = "rows" in result && Array.isArray(result.rows) ? result.rows : [];
3763
+ const definition = rows[0]?.constraint_definition;
3764
+ if (!definition) {
3765
+ throw new Error(`Could not read canonical check definition for expression: ${expression}`);
3864
3766
  }
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;
3767
+ return this.normalizeCheckExpression(this.extractCheckExpressionFromDefinition(definition));
3768
+ } finally {
3769
+ try {
3770
+ await trx.rollback();
3771
+ } catch {
3869
3772
  }
3870
3773
  }
3871
- parts.push(s.slice(start).trim());
3872
- return parts.filter(Boolean);
3873
3774
  }
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);
3883
- }
3884
- normalizeExcludeDef(def) {
3885
- const s = def.replace(/\s+/g, " ").replace(/\s*\(\s*/g, "(").replace(/\s*\)\s*/g, ")").trim();
3886
- return this.normalizeSqlIdentifiers(s);
3887
- }
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();
3775
+ extractCheckExpressionFromDefinition(definition) {
3776
+ const trimmed = definition.trim();
3777
+ const match = trimmed.match(/^CHECK\s*\(([\s\S]*)\)$/i);
3778
+ if (!match) {
3779
+ return trimmed;
3780
+ }
3781
+ return match[1];
3782
+ }
3783
+ quoteIdentifier(identifier) {
3784
+ return `"${identifier.replace(/"/g, '""')}"`;
3785
+ }
3786
+ quoteQualifiedIdentifier(identifier) {
3787
+ return identifier.split(".").map((part) => this.quoteIdentifier(part)).join(".");
3890
3788
  }
3891
3789
  /** Escape expression for embedding inside a template literal in generated code */
3892
3790
  escapeExpressionForRaw(expr) {
3893
3791
  return expr.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$");
3894
3792
  }
3895
- addCheckConstraint(table, constraintName, expression, deferrable) {
3793
+ addCheckConstraint(table, constraintName, expression) {
3896
3794
  const escaped = this.escapeExpressionForRaw(expression);
3897
- const deferrableClause = deferrable ? ` DEFERRABLE ${deferrable}` : "";
3898
3795
  this.writer.writeLine(
3899
- `await knex.raw(\`ALTER TABLE "${table}" ADD CONSTRAINT "${constraintName}" CHECK (${escaped})${deferrableClause}\`);`
3796
+ `await knex.raw(\`ALTER TABLE "${table}" ADD CONSTRAINT "${constraintName}" CHECK (${escaped})\`);`
3900
3797
  );
3901
3798
  this.writer.blankLine();
3902
3799
  }
@@ -3904,43 +3801,6 @@ var MigrationGenerator = class _MigrationGenerator {
3904
3801
  this.writer.writeLine(`await knex.raw('ALTER TABLE "${table}" DROP CONSTRAINT "${constraintName}"');`);
3905
3802
  this.writer.blankLine();
3906
3803
  }
3907
- buildExcludeDef(entry) {
3908
- const elementsStr = entry.elements.map((el) => "column" in el ? `"${el.column}" WITH ${el.operator}` : `${el.expression} WITH ${el.operator}`).join(", ");
3909
- const whereClause = entry.where ? ` WHERE (${entry.where})` : "";
3910
- const deferrableClause = entry.deferrable ? ` DEFERRABLE ${entry.deferrable}` : "";
3911
- return `EXCLUDE USING ${entry.using} (${elementsStr})${whereClause}${deferrableClause}`;
3912
- }
3913
- addExcludeConstraint(table, constraintName, entry) {
3914
- const def = this.buildExcludeDef(entry);
3915
- const escaped = this.escapeExpressionForRaw(def);
3916
- this.writer.writeLine(`await knex.raw(\`ALTER TABLE "${table}" ADD CONSTRAINT "${constraintName}" ${escaped}\`);`);
3917
- this.writer.blankLine();
3918
- }
3919
- dropExcludeConstraint(table, constraintName) {
3920
- this.writer.writeLine(`await knex.raw('ALTER TABLE "${table}" DROP CONSTRAINT "${constraintName}"');`);
3921
- this.writer.blankLine();
3922
- }
3923
- buildConstraintTriggerDef(table, constraintName, entry) {
3924
- const eventsStr = entry.events.join(" OR ");
3925
- const deferrableClause = entry.deferrable ? ` DEFERRABLE ${entry.deferrable}` : "";
3926
- const argsStr = entry.function.args?.length ? entry.function.args.map((a) => `"${a}"`).join(", ") : "";
3927
- const executeClause = argsStr ? `EXECUTE FUNCTION ${entry.function.name}(${argsStr})` : `EXECUTE FUNCTION ${entry.function.name}()`;
3928
- return `CREATE CONSTRAINT TRIGGER "${constraintName}" ${entry.when} ${eventsStr} ON "${table}"${deferrableClause} FOR EACH ${entry.forEach} ${executeClause}`;
3929
- }
3930
- addConstraintTrigger(table, constraintName, entry) {
3931
- const eventsStr = entry.events.join(" OR ");
3932
- const deferrableClause = entry.deferrable ? ` DEFERRABLE ${entry.deferrable}` : "";
3933
- const argsStr = entry.function.args?.length ? entry.function.args.map((a) => `"${a}"`).join(", ") : "";
3934
- const executeClause = argsStr ? `EXECUTE FUNCTION ${entry.function.name}(${argsStr})` : `EXECUTE FUNCTION ${entry.function.name}()`;
3935
- this.writer.writeLine(
3936
- `await knex.raw(\`CREATE CONSTRAINT TRIGGER "${constraintName}" ${entry.when} ${eventsStr} ON "${table}"${deferrableClause} FOR EACH ${entry.forEach} ${executeClause}\`);`
3937
- );
3938
- this.writer.blankLine();
3939
- }
3940
- dropConstraintTrigger(table, constraintName) {
3941
- this.writer.writeLine(`await knex.raw('ALTER TABLE "${table}" DROP CONSTRAINT "${constraintName}"');`);
3942
- this.writer.blankLine();
3943
- }
3944
3804
  value(value2) {
3945
3805
  if (typeof value2 === "string") {
3946
3806
  return `'${value2}'`;
@@ -4967,6 +4827,5 @@ var printSchemaFromModels = (models) => printSchema((0, import_graphql6.buildAST
4967
4827
  updateEntity,
4968
4828
  updateFunctions,
4969
4829
  validateCheckConstraint,
4970
- validateExcludeConstraint,
4971
4830
  value
4972
4831
  });
@@ -6,7 +6,6 @@ export const generateFunctionsFromDatabase = async (knex) => {
6
6
  JOIN pg_namespace n ON p.pronamespace = n.oid
7
7
  WHERE n.nspname = 'public'
8
8
  AND NOT EXISTS (SELECT 1 FROM pg_aggregate a WHERE a.aggfnoid = p.oid)
9
- AND NOT EXISTS (SELECT 1 FROM pg_depend d WHERE d.objid = p.oid AND d.deptype = 'e')
10
9
  ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
11
10
  `);
12
11
  const aggregateFunctions = await knex.raw(`
@@ -21,7 +20,6 @@ export const generateFunctionsFromDatabase = async (knex) => {
21
20
  JOIN pg_aggregate a ON p.oid = a.aggfnoid
22
21
  JOIN pg_namespace n ON p.pronamespace = n.oid
23
22
  WHERE n.nspname = 'public'
24
- AND NOT EXISTS (SELECT 1 FROM pg_depend d WHERE d.objid = p.oid AND d.deptype = 'e')
25
23
  ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
26
24
  `);
27
25
  const functions = [];
@@ -1 +1 @@
1
- {"version":3,"file":"generate-functions.js","sourceRoot":"","sources":["../../../src/migrations/generate-functions.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,6BAA6B,GAAG,KAAK,EAAE,IAAU,EAAmB,EAAE;IACjF,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC;;;;;;;;;GASvC,CAAC,CAAC;IAEH,MAAM,kBAAkB,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC;;;;;;;;;;;;;;GAczC,CAAC,CAAC;IAEH,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,KAAK,MAAM,GAAG,IAAI,gBAAgB,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;QAC9C,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;YACnB,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,kBAAkB,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;QAChD,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;QAC5B,MAAM,YAAY,GAAG,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC;QACzC,MAAM,SAAS,GAAG,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC;QACvC,MAAM,SAAS,GAAG,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC;QACvC,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC;QAC7B,MAAM,SAAS,GAAG,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC;QAEvC,IAAI,CAAC,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS,EAAE,CAAC;YACtC,SAAS;QACX,CAAC;QAED,IAAI,YAAY,GAAG,oBAAoB,IAAI,IAAI,YAAY,OAAO,CAAC;QACnE,YAAY,IAAI,aAAa,SAAS,KAAK,CAAC;QAC5C,YAAY,IAAI,aAAa,SAAS,EAAE,CAAC;QAEzC,IAAI,SAAS,EAAE,CAAC;YACd,YAAY,IAAI,oBAAoB,SAAS,EAAE,CAAC;QAClD,CAAC;QAED,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC9C,MAAM,UAAU,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAClF,YAAY,IAAI,mBAAmB,UAAU,EAAE,CAAC;QAClD,CAAC;QAED,YAAY,IAAI,MAAM,CAAC;QAEvB,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,0CAA0C,CAAC;IACpD,CAAC;IAED,MAAM,oBAAoB,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAE9F,OAAO,yCAAyC,oBAAoB,SAAS,CAAC;AAChF,CAAC,CAAC"}
1
+ {"version":3,"file":"generate-functions.js","sourceRoot":"","sources":["../../../src/migrations/generate-functions.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,6BAA6B,GAAG,KAAK,EAAE,IAAU,EAAmB,EAAE;IACjF,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC;;;;;;;;GAQvC,CAAC,CAAC;IAEH,MAAM,kBAAkB,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC;;;;;;;;;;;;;GAazC,CAAC,CAAC;IAEH,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,KAAK,MAAM,GAAG,IAAI,gBAAgB,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;QAC9C,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;YACnB,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,kBAAkB,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;QAChD,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;QAC5B,MAAM,YAAY,GAAG,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC;QACzC,MAAM,SAAS,GAAG,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC;QACvC,MAAM,SAAS,GAAG,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC;QACvC,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC;QAC7B,MAAM,SAAS,GAAG,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC;QAEvC,IAAI,CAAC,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS,EAAE,CAAC;YACtC,SAAS;QACX,CAAC;QAED,IAAI,YAAY,GAAG,oBAAoB,IAAI,IAAI,YAAY,OAAO,CAAC;QACnE,YAAY,IAAI,aAAa,SAAS,KAAK,CAAC;QAC5C,YAAY,IAAI,aAAa,SAAS,EAAE,CAAC;QAEzC,IAAI,SAAS,EAAE,CAAC;YACd,YAAY,IAAI,oBAAoB,SAAS,EAAE,CAAC;QAClD,CAAC;QAED,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC9C,MAAM,UAAU,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAClF,YAAY,IAAI,mBAAmB,UAAU,EAAE,CAAC;QAClD,CAAC;QAED,YAAY,IAAI,MAAM,CAAC;QAEvB,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,0CAA0C,CAAC;IACpD,CAAC;IAED,MAAM,oBAAoB,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAE9F,OAAO,yCAAyC,oBAAoB,SAAS,CAAC;AAChF,CAAC,CAAC"}
@@ -9,10 +9,6 @@ export declare class MigrationGenerator {
9
9
  private columns;
10
10
  /** table name -> constraint name -> check clause expression */
11
11
  private existingCheckConstraints;
12
- /** table name -> constraint name -> { normalized, raw } */
13
- private existingExcludeConstraints;
14
- /** table name -> constraint name -> { normalized, raw } */
15
- private existingConstraintTriggers;
16
12
  private uuidUsed?;
17
13
  private nowUsed?;
18
14
  needsMigration: boolean;
@@ -34,25 +30,19 @@ export declare class MigrationGenerator {
34
30
  private dropTable;
35
31
  private renameTable;
36
32
  private renameColumn;
37
- private getConstraintName;
38
- private static readonly SQL_KEYWORDS;
39
- private static readonly LITERAL_PLACEHOLDER;
40
- private normalizeSqlIdentifiers;
41
- private stripOuterParens;
42
- private splitAtTopLevel;
33
+ private getCheckConstraintName;
43
34
  private normalizeCheckExpression;
44
- private normalizeExcludeDef;
45
- private normalizeTriggerDef;
35
+ private isWrappedByOuterParentheses;
36
+ private findExistingConstraint;
37
+ private equalExpressions;
38
+ private canonicalizeCheckExpressionWithPostgres;
39
+ private extractCheckExpressionFromDefinition;
40
+ private quoteIdentifier;
41
+ private quoteQualifiedIdentifier;
46
42
  /** Escape expression for embedding inside a template literal in generated code */
47
43
  private escapeExpressionForRaw;
48
44
  private addCheckConstraint;
49
45
  private dropCheckConstraint;
50
- private buildExcludeDef;
51
- private addExcludeConstraint;
52
- private dropExcludeConstraint;
53
- private buildConstraintTriggerDef;
54
- private addConstraintTrigger;
55
- private dropConstraintTrigger;
56
46
  private value;
57
47
  private columnRaw;
58
48
  private column;