@smartive/graphql-magic 23.7.0 → 23.8.0-next.2

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,6 +198,7 @@ __export(index_exports, {
198
198
  updateEntity: () => updateEntity,
199
199
  updateFunctions: () => updateFunctions,
200
200
  validateCheckConstraint: () => validateCheckConstraint,
201
+ validateExcludeConstraint: () => validateExcludeConstraint,
201
202
  value: () => value
202
203
  });
203
204
  module.exports = __toCommonJS(index_exports);
@@ -654,6 +655,8 @@ var EntityModel = class extends Model {
654
655
  for (const constraint of this.constraints) {
655
656
  if (constraint.kind === "check") {
656
657
  validateCheckConstraint(this, constraint);
658
+ } else if (constraint.kind === "exclude") {
659
+ validateExcludeConstraint(this, constraint);
657
660
  }
658
661
  }
659
662
  }
@@ -952,6 +955,19 @@ var validateCheckConstraint = (model, constraint) => {
952
955
  }
953
956
  }
954
957
  };
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
+ };
955
971
 
956
972
  // src/client/queries.ts
957
973
  var fieldIsSearchable = (model, fieldName) => {
@@ -1166,6 +1182,7 @@ var generateFunctionsFromDatabase = async (knex) => {
1166
1182
  JOIN pg_namespace n ON p.pronamespace = n.oid
1167
1183
  WHERE n.nspname = 'public'
1168
1184
  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')
1169
1186
  ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
1170
1187
  `);
1171
1188
  const aggregateFunctions = await knex.raw(`
@@ -1180,6 +1197,7 @@ var generateFunctionsFromDatabase = async (knex) => {
1180
1197
  JOIN pg_aggregate a ON p.oid = a.aggfnoid
1181
1198
  JOIN pg_namespace n ON p.pronamespace = n.oid
1182
1199
  WHERE n.nspname = 'public'
1200
+ AND NOT EXISTS (SELECT 1 FROM pg_depend d WHERE d.objid = p.oid AND d.deptype = 'e')
1183
1201
  ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
1184
1202
  `);
1185
1203
  const functions = [];
@@ -2924,6 +2942,7 @@ var getDatabaseFunctions = async (knex) => {
2924
2942
  JOIN pg_namespace n ON p.pronamespace = n.oid
2925
2943
  WHERE n.nspname = 'public'
2926
2944
  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')
2927
2946
  ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
2928
2947
  `);
2929
2948
  const aggregateFunctions = await knex.raw(`
@@ -2939,6 +2958,7 @@ var getDatabaseFunctions = async (knex) => {
2939
2958
  JOIN pg_aggregate a ON p.oid = a.aggfnoid
2940
2959
  JOIN pg_namespace n ON p.pronamespace = n.oid
2941
2960
  WHERE n.nspname = 'public'
2961
+ AND NOT EXISTS (SELECT 1 FROM pg_depend d WHERE d.objid = p.oid AND d.deptype = 'e')
2942
2962
  ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
2943
2963
  `);
2944
2964
  const result = [];
@@ -3082,7 +3102,7 @@ Summary: ${updatedCount} updated, ${skippedCount} skipped`);
3082
3102
  };
3083
3103
 
3084
3104
  // src/migrations/generate.ts
3085
- var MigrationGenerator = class {
3105
+ var MigrationGenerator = class _MigrationGenerator {
3086
3106
  constructor(knex, models, parsedFunctions) {
3087
3107
  this.models = models;
3088
3108
  this.parsedFunctions = parsedFunctions;
@@ -3098,6 +3118,10 @@ var MigrationGenerator = class {
3098
3118
  columns = {};
3099
3119
  /** table name -> constraint name -> check clause expression */
3100
3120
  existingCheckConstraints = {};
3121
+ /** table name -> constraint name -> { normalized, raw } */
3122
+ existingExcludeConstraints = {};
3123
+ /** table name -> constraint name -> { normalized, raw } */
3124
+ existingConstraintTriggers = {};
3101
3125
  uuidUsed;
3102
3126
  nowUsed;
3103
3127
  needsMigration = false;
@@ -3125,8 +3149,56 @@ var MigrationGenerator = class {
3125
3149
  }
3126
3150
  this.existingCheckConstraints[tableName].set(row.constraint_name, row.check_clause);
3127
3151
  }
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
+ }
3128
3187
  const up = [];
3129
3188
  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
+ }
3130
3202
  this.createEnums(
3131
3203
  this.models.enums.filter((enm2) => !enums.includes((0, import_lowerFirst.default)(enm2.name))),
3132
3204
  up,
@@ -3205,10 +3277,22 @@ var MigrationGenerator = class {
3205
3277
  if (entry.kind === "check") {
3206
3278
  validateCheckConstraint(model, entry);
3207
3279
  const table = model.name;
3208
- const constraintName = this.getCheckConstraintName(model, entry, i);
3209
- const expression = entry.expression;
3280
+ const constraintName = this.getConstraintName(model, entry, i);
3281
+ 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);
3210
3288
  up.push(() => {
3211
- this.addCheckConstraint(table, constraintName, expression);
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);
3212
3296
  });
3213
3297
  }
3214
3298
  }
@@ -3248,36 +3332,84 @@ var MigrationGenerator = class {
3248
3332
  );
3249
3333
  this.updateFields(model, existingFields, up, down);
3250
3334
  if (model.constraints?.length) {
3335
+ const existingExcludeMap = this.existingExcludeConstraints[model.name];
3336
+ const existingTriggerMap = this.existingConstraintTriggers[model.name];
3251
3337
  for (let i = 0; i < model.constraints.length; i++) {
3252
3338
  const entry = model.constraints[i];
3253
- if (entry.kind !== "check") {
3254
- continue;
3255
- }
3256
- validateCheckConstraint(model, entry);
3257
3339
  const table = model.name;
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
- });
3340
+ const constraintName = this.getConstraintName(model, entry, i);
3341
+ if (entry.kind === "check") {
3342
+ validateCheckConstraint(model, entry);
3343
+ const existingConstraint = this.findExistingConstraint(table, entry, constraintName);
3344
+ if (!existingConstraint) {
3345
+ up.push(() => {
3346
+ this.addCheckConstraint(table, constraintName, entry.expression, entry.deferrable);
3347
+ });
3348
+ down.push(() => {
3349
+ this.dropCheckConstraint(table, constraintName);
3350
+ });
3351
+ } else if (!await this.equalExpressions(
3352
+ table,
3353
+ existingConstraint.constraintName,
3354
+ existingConstraint.expression,
3355
+ entry.expression
3356
+ )) {
3357
+ up.push(() => {
3358
+ this.dropCheckConstraint(table, existingConstraint.constraintName);
3359
+ this.addCheckConstraint(table, constraintName, entry.expression, entry.deferrable);
3360
+ });
3361
+ down.push(() => {
3362
+ this.dropCheckConstraint(table, constraintName);
3363
+ this.addCheckConstraint(table, existingConstraint.constraintName, existingConstraint.expression);
3364
+ });
3365
+ }
3366
+ } else if (entry.kind === "exclude") {
3367
+ validateExcludeConstraint(model, entry);
3368
+ const newDef = this.normalizeExcludeDef(this.buildExcludeDef(entry));
3369
+ const existing = existingExcludeMap?.get(constraintName);
3370
+ if (existing === void 0) {
3371
+ up.push(() => {
3372
+ this.addExcludeConstraint(table, constraintName, entry);
3373
+ });
3374
+ down.push(() => {
3375
+ this.dropExcludeConstraint(table, constraintName);
3376
+ });
3377
+ } else if (existing.normalized !== newDef) {
3378
+ up.push(() => {
3379
+ this.dropExcludeConstraint(table, constraintName);
3380
+ this.addExcludeConstraint(table, constraintName, entry);
3381
+ });
3382
+ down.push(() => {
3383
+ this.dropExcludeConstraint(table, constraintName);
3384
+ const escaped = this.escapeExpressionForRaw(existing.raw);
3385
+ this.writer.writeLine(
3386
+ `await knex.raw(\`ALTER TABLE "${table}" ADD CONSTRAINT "${constraintName}" ${escaped}\`);`
3387
+ );
3388
+ this.writer.blankLine();
3389
+ });
3390
+ }
3391
+ } else if (entry.kind === "constraint_trigger") {
3392
+ const newDef = this.normalizeTriggerDef(this.buildConstraintTriggerDef(table, constraintName, entry));
3393
+ const existing = existingTriggerMap?.get(constraintName);
3394
+ if (existing === void 0) {
3395
+ up.push(() => {
3396
+ this.addConstraintTrigger(table, constraintName, entry);
3397
+ });
3398
+ down.push(() => {
3399
+ this.dropConstraintTrigger(table, constraintName);
3400
+ });
3401
+ } else if (existing.normalized !== newDef) {
3402
+ up.push(() => {
3403
+ this.dropConstraintTrigger(table, constraintName);
3404
+ this.addConstraintTrigger(table, constraintName, entry);
3405
+ });
3406
+ down.push(() => {
3407
+ this.dropConstraintTrigger(table, constraintName);
3408
+ const escaped = existing.raw.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$");
3409
+ this.writer.writeLine(`await knex.raw(\`${escaped}\`);`);
3410
+ this.writer.blankLine();
3411
+ });
3412
+ }
3281
3413
  }
3282
3414
  }
3283
3415
  }
@@ -3652,9 +3784,113 @@ var MigrationGenerator = class {
3652
3784
  renameColumn(from, to) {
3653
3785
  this.writer.writeLine(`table.renameColumn('${from}', '${to}');`);
3654
3786
  }
3655
- getCheckConstraintName(model, entry, index) {
3787
+ getConstraintName(model, entry, index) {
3656
3788
  return `${model.name}_${entry.name}_${entry.kind}_${index}`;
3657
3789
  }
3790
+ static SQL_KEYWORDS = /* @__PURE__ */ new Set([
3791
+ "and",
3792
+ "or",
3793
+ "not",
3794
+ "in",
3795
+ "is",
3796
+ "null",
3797
+ "true",
3798
+ "false",
3799
+ "between",
3800
+ "like",
3801
+ "exists",
3802
+ "all",
3803
+ "any",
3804
+ "asc",
3805
+ "desc",
3806
+ "with",
3807
+ "using",
3808
+ "as",
3809
+ "on",
3810
+ "infinity",
3811
+ "extract",
3812
+ "current_date",
3813
+ "current_timestamp"
3814
+ ]);
3815
+ static LITERAL_PLACEHOLDER = "\uE000";
3816
+ static IDENT_PLACEHOLDER = "\uE001";
3817
+ normalizeSqlIdentifiers(s) {
3818
+ const literals = [];
3819
+ let result = s.replace(/'([^']|'')*'/g, (lit) => {
3820
+ literals.push(lit);
3821
+ return `${_MigrationGenerator.LITERAL_PLACEHOLDER}${literals.length - 1}${_MigrationGenerator.LITERAL_PLACEHOLDER}`;
3822
+ });
3823
+ const quotedIdents = [];
3824
+ result = result.replace(/"([^"]*)"/g, (_, ident) => {
3825
+ quotedIdents.push(`"${ident.toLowerCase()}"`);
3826
+ return `${_MigrationGenerator.IDENT_PLACEHOLDER}${quotedIdents.length - 1}${_MigrationGenerator.IDENT_PLACEHOLDER}`;
3827
+ });
3828
+ result = result.replace(
3829
+ /\b([a-zA-Z_][a-zA-Z0-9_]*)\b(?!\s*\()/g,
3830
+ (match) => _MigrationGenerator.SQL_KEYWORDS.has(match.toLowerCase()) ? match : `"${match.toLowerCase()}"`
3831
+ );
3832
+ for (let i = 0; i < quotedIdents.length; i++) {
3833
+ result = result.replace(
3834
+ new RegExp(`${_MigrationGenerator.IDENT_PLACEHOLDER}${i}${_MigrationGenerator.IDENT_PLACEHOLDER}`, "g"),
3835
+ quotedIdents[i]
3836
+ );
3837
+ }
3838
+ for (let i = 0; i < literals.length; i++) {
3839
+ result = result.replace(
3840
+ new RegExp(`${_MigrationGenerator.LITERAL_PLACEHOLDER}${i}${_MigrationGenerator.LITERAL_PLACEHOLDER}`, "g"),
3841
+ literals[i]
3842
+ );
3843
+ }
3844
+ return result;
3845
+ }
3846
+ normalizeExcludeDef(def) {
3847
+ let s = def.replace(/\s+/g, " ").replace(/\s*\(\s*/g, "(").replace(/\s*\)\s*/g, ")").replace(/::\s*timestamp\s+with\s+time\s+zone\b/gi, "::timestamptz").replace(/::\s*timestamp\s+without\s+time\s+zone\b/gi, "::timestamp").trim();
3848
+ const whereMatch = s.match(/\bWHERE\s*\(/i);
3849
+ if (whereMatch) {
3850
+ const openParen = (whereMatch.index ?? 0) + whereMatch[0].length - 1;
3851
+ const closeParen = this.findMatchingParen(s, openParen);
3852
+ if (closeParen !== -1) {
3853
+ let cond = s.slice(openParen + 1, closeParen).trim();
3854
+ while (this.isWrappedByOuterParentheses(cond)) {
3855
+ cond = cond.slice(1, -1).trim();
3856
+ }
3857
+ s = s.slice(0, openParen + 1) + cond + s.slice(closeParen);
3858
+ }
3859
+ }
3860
+ return this.normalizeSqlIdentifiers(s);
3861
+ }
3862
+ findMatchingParen(s, openIndex) {
3863
+ let depth = 1;
3864
+ let inSingleQuote = false;
3865
+ for (let i = openIndex + 1; i < s.length; i++) {
3866
+ const char = s[i];
3867
+ const next = s[i + 1];
3868
+ if (char === "'") {
3869
+ if (inSingleQuote && next === "'") {
3870
+ i++;
3871
+ continue;
3872
+ }
3873
+ inSingleQuote = !inSingleQuote;
3874
+ continue;
3875
+ }
3876
+ if (inSingleQuote) {
3877
+ continue;
3878
+ }
3879
+ if (char === "(") {
3880
+ depth++;
3881
+ } else if (char === ")") {
3882
+ depth--;
3883
+ if (depth === 0) {
3884
+ return i;
3885
+ }
3886
+ }
3887
+ }
3888
+ return -1;
3889
+ }
3890
+ normalizeTriggerDef(def) {
3891
+ 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();
3892
+ return this.normalizeSqlIdentifiers(s);
3893
+ }
3658
3894
  normalizeCheckExpression(expr) {
3659
3895
  let normalized = expr.replace(/\s+/g, " ").trim();
3660
3896
  while (this.isWrappedByOuterParentheses(normalized)) {
@@ -3790,10 +4026,11 @@ var MigrationGenerator = class {
3790
4026
  escapeExpressionForRaw(expr) {
3791
4027
  return expr.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$");
3792
4028
  }
3793
- addCheckConstraint(table, constraintName, expression) {
4029
+ addCheckConstraint(table, constraintName, expression, deferrable) {
3794
4030
  const escaped = this.escapeExpressionForRaw(expression);
4031
+ const deferrableClause = deferrable ? ` DEFERRABLE ${deferrable}` : "";
3795
4032
  this.writer.writeLine(
3796
- `await knex.raw(\`ALTER TABLE "${table}" ADD CONSTRAINT "${constraintName}" CHECK (${escaped})\`);`
4033
+ `await knex.raw(\`ALTER TABLE "${table}" ADD CONSTRAINT "${constraintName}" CHECK (${escaped})${deferrableClause}\`);`
3797
4034
  );
3798
4035
  this.writer.blankLine();
3799
4036
  }
@@ -3801,6 +4038,43 @@ var MigrationGenerator = class {
3801
4038
  this.writer.writeLine(`await knex.raw('ALTER TABLE "${table}" DROP CONSTRAINT "${constraintName}"');`);
3802
4039
  this.writer.blankLine();
3803
4040
  }
4041
+ buildExcludeDef(entry) {
4042
+ const elementsStr = entry.elements.map((el) => "column" in el ? `"${el.column}" WITH ${el.operator}` : `${el.expression} WITH ${el.operator}`).join(", ");
4043
+ const whereClause = entry.where ? ` WHERE (${entry.where})` : "";
4044
+ const deferrableClause = entry.deferrable ? ` DEFERRABLE ${entry.deferrable}` : "";
4045
+ return `EXCLUDE USING ${entry.using} (${elementsStr})${whereClause}${deferrableClause}`;
4046
+ }
4047
+ addExcludeConstraint(table, constraintName, entry) {
4048
+ const def = this.buildExcludeDef(entry);
4049
+ const escaped = this.escapeExpressionForRaw(def);
4050
+ this.writer.writeLine(`await knex.raw(\`ALTER TABLE "${table}" ADD CONSTRAINT "${constraintName}" ${escaped}\`);`);
4051
+ this.writer.blankLine();
4052
+ }
4053
+ dropExcludeConstraint(table, constraintName) {
4054
+ this.writer.writeLine(`await knex.raw('ALTER TABLE "${table}" DROP CONSTRAINT "${constraintName}"');`);
4055
+ this.writer.blankLine();
4056
+ }
4057
+ buildConstraintTriggerDef(table, constraintName, entry) {
4058
+ const eventsStr = entry.events.join(" OR ");
4059
+ const deferrableClause = entry.deferrable ? ` DEFERRABLE ${entry.deferrable}` : "";
4060
+ const argsStr = entry.function.args?.length ? entry.function.args.map((a) => `"${a}"`).join(", ") : "";
4061
+ const executeClause = argsStr ? `EXECUTE FUNCTION ${entry.function.name}(${argsStr})` : `EXECUTE FUNCTION ${entry.function.name}()`;
4062
+ return `CREATE CONSTRAINT TRIGGER "${constraintName}" ${entry.when} ${eventsStr} ON "${table}"${deferrableClause} FOR EACH ${entry.forEach} ${executeClause}`;
4063
+ }
4064
+ addConstraintTrigger(table, constraintName, entry) {
4065
+ const eventsStr = entry.events.join(" OR ");
4066
+ const deferrableClause = entry.deferrable ? ` DEFERRABLE ${entry.deferrable}` : "";
4067
+ const argsStr = entry.function.args?.length ? entry.function.args.map((a) => `"${a}"`).join(", ") : "";
4068
+ const executeClause = argsStr ? `EXECUTE FUNCTION ${entry.function.name}(${argsStr})` : `EXECUTE FUNCTION ${entry.function.name}()`;
4069
+ this.writer.writeLine(
4070
+ `await knex.raw(\`CREATE CONSTRAINT TRIGGER "${constraintName}" ${entry.when} ${eventsStr} ON "${table}"${deferrableClause} FOR EACH ${entry.forEach} ${executeClause}\`);`
4071
+ );
4072
+ this.writer.blankLine();
4073
+ }
4074
+ dropConstraintTrigger(table, constraintName) {
4075
+ this.writer.writeLine(`await knex.raw('ALTER TABLE "${table}" DROP CONSTRAINT "${constraintName}"');`);
4076
+ this.writer.blankLine();
4077
+ }
3804
4078
  value(value2) {
3805
4079
  if (typeof value2 === "string") {
3806
4080
  return `'${value2}'`;
@@ -4827,5 +5101,6 @@ var printSchemaFromModels = (models) => printSchema((0, import_graphql6.buildAST
4827
5101
  updateEntity,
4828
5102
  updateFunctions,
4829
5103
  validateCheckConstraint,
5104
+ validateExcludeConstraint,
4830
5105
  value
4831
5106
  });
@@ -6,6 +6,7 @@ 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')
9
10
  ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
10
11
  `);
11
12
  const aggregateFunctions = await knex.raw(`
@@ -20,6 +21,7 @@ export const generateFunctionsFromDatabase = async (knex) => {
20
21
  JOIN pg_aggregate a ON p.oid = a.aggfnoid
21
22
  JOIN pg_namespace n ON p.pronamespace = n.oid
22
23
  WHERE n.nspname = 'public'
24
+ AND NOT EXISTS (SELECT 1 FROM pg_depend d WHERE d.objid = p.oid AND d.deptype = 'e')
23
25
  ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
24
26
  `);
25
27
  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;;;;;;;;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"}
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"}
@@ -9,6 +9,10 @@ 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;
12
16
  private uuidUsed?;
13
17
  private nowUsed?;
14
18
  needsMigration: boolean;
@@ -30,7 +34,14 @@ export declare class MigrationGenerator {
30
34
  private dropTable;
31
35
  private renameTable;
32
36
  private renameColumn;
33
- private getCheckConstraintName;
37
+ private getConstraintName;
38
+ private static readonly SQL_KEYWORDS;
39
+ private static readonly LITERAL_PLACEHOLDER;
40
+ private static readonly IDENT_PLACEHOLDER;
41
+ private normalizeSqlIdentifiers;
42
+ private normalizeExcludeDef;
43
+ private findMatchingParen;
44
+ private normalizeTriggerDef;
34
45
  private normalizeCheckExpression;
35
46
  private isWrappedByOuterParentheses;
36
47
  private findExistingConstraint;
@@ -43,6 +54,12 @@ export declare class MigrationGenerator {
43
54
  private escapeExpressionForRaw;
44
55
  private addCheckConstraint;
45
56
  private dropCheckConstraint;
57
+ private buildExcludeDef;
58
+ private addExcludeConstraint;
59
+ private dropExcludeConstraint;
60
+ private buildConstraintTriggerDef;
61
+ private addConstraintTrigger;
62
+ private dropConstraintTrigger;
46
63
  private value;
47
64
  private columnRaw;
48
65
  private column;