@smartive/graphql-magic 23.7.0 → 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 +9 -2
- package/dist/bin/gqm.cjs +260 -34
- package/dist/cjs/index.cjs +262 -34
- package/dist/esm/migrations/generate-functions.js +2 -0
- package/dist/esm/migrations/generate-functions.js.map +1 -1
- package/dist/esm/migrations/generate.d.ts +16 -1
- package/dist/esm/migrations/generate.js +241 -29
- package/dist/esm/migrations/generate.js.map +1 -1
- package/dist/esm/migrations/update-functions.js +2 -0
- package/dist/esm/migrations/update-functions.js.map +1 -1
- package/dist/esm/models/model-definitions.d.ts +27 -2
- package/dist/esm/models/models.d.ts +1 -5
- package/dist/esm/models/models.js +4 -1
- package/dist/esm/models/models.js.map +1 -1
- package/dist/esm/models/utils.d.ts +10 -0
- package/dist/esm/models/utils.js +11 -0
- package/dist/esm/models/utils.js.map +1 -1
- package/docs/docs/2-models.md +18 -4
- package/docs/docs/5-migrations.md +11 -5
- package/package.json +1 -1
- package/src/bin/gqm/parse-knexfile.ts +1 -0
- package/src/bin/gqm/settings.ts +4 -0
- package/src/migrations/generate-functions.ts +2 -0
- package/src/migrations/generate.ts +322 -35
- package/src/migrations/update-functions.ts +2 -0
- package/src/models/model-definitions.ts +20 -1
- package/src/models/models.ts +4 -1
- package/src/models/utils.ts +20 -0
- package/tests/unit/constraints.spec.ts +98 -2
package/dist/cjs/index.cjs
CHANGED
|
@@ -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.
|
|
3209
|
-
const expression = entry.expression;
|
|
3280
|
+
const constraintName = this.getConstraintName(model, entry, i);
|
|
3210
3281
|
up.push(() => {
|
|
3211
|
-
this.addCheckConstraint(table, constraintName, expression);
|
|
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);
|
|
3212
3296
|
});
|
|
3213
3297
|
}
|
|
3214
3298
|
}
|
|
@@ -3248,36 +3332,88 @@ 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.
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
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(
|
|
3364
|
+
table,
|
|
3365
|
+
existingConstraint.constraintName,
|
|
3366
|
+
existingConstraint.expression
|
|
3367
|
+
);
|
|
3368
|
+
});
|
|
3369
|
+
}
|
|
3370
|
+
} else if (entry.kind === "exclude") {
|
|
3371
|
+
validateExcludeConstraint(model, entry);
|
|
3372
|
+
const newDef = this.normalizeExcludeDef(this.buildExcludeDef(entry));
|
|
3373
|
+
const existing = existingExcludeMap?.get(constraintName);
|
|
3374
|
+
if (existing === void 0) {
|
|
3375
|
+
up.push(() => {
|
|
3376
|
+
this.addExcludeConstraint(table, constraintName, entry);
|
|
3377
|
+
});
|
|
3378
|
+
down.push(() => {
|
|
3379
|
+
this.dropExcludeConstraint(table, constraintName);
|
|
3380
|
+
});
|
|
3381
|
+
} else if (existing.normalized !== newDef) {
|
|
3382
|
+
up.push(() => {
|
|
3383
|
+
this.dropExcludeConstraint(table, constraintName);
|
|
3384
|
+
this.addExcludeConstraint(table, constraintName, entry);
|
|
3385
|
+
});
|
|
3386
|
+
down.push(() => {
|
|
3387
|
+
this.dropExcludeConstraint(table, constraintName);
|
|
3388
|
+
const escaped = this.escapeExpressionForRaw(existing.raw);
|
|
3389
|
+
this.writer.writeLine(
|
|
3390
|
+
`await knex.raw(\`ALTER TABLE "${table}" ADD CONSTRAINT "${constraintName}" ${escaped}\`);`
|
|
3391
|
+
);
|
|
3392
|
+
this.writer.blankLine();
|
|
3393
|
+
});
|
|
3394
|
+
}
|
|
3395
|
+
} else if (entry.kind === "constraint_trigger") {
|
|
3396
|
+
const newDef = this.normalizeTriggerDef(this.buildConstraintTriggerDef(table, constraintName, entry));
|
|
3397
|
+
const existing = existingTriggerMap?.get(constraintName);
|
|
3398
|
+
if (existing === void 0) {
|
|
3399
|
+
up.push(() => {
|
|
3400
|
+
this.addConstraintTrigger(table, constraintName, entry);
|
|
3401
|
+
});
|
|
3402
|
+
down.push(() => {
|
|
3403
|
+
this.dropConstraintTrigger(table, constraintName);
|
|
3404
|
+
});
|
|
3405
|
+
} else if (existing.normalized !== newDef) {
|
|
3406
|
+
up.push(() => {
|
|
3407
|
+
this.dropConstraintTrigger(table, constraintName);
|
|
3408
|
+
this.addConstraintTrigger(table, constraintName, entry);
|
|
3409
|
+
});
|
|
3410
|
+
down.push(() => {
|
|
3411
|
+
this.dropConstraintTrigger(table, constraintName);
|
|
3412
|
+
const escaped = existing.raw.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$");
|
|
3413
|
+
this.writer.writeLine(`await knex.raw(\`${escaped}\`);`);
|
|
3414
|
+
this.writer.blankLine();
|
|
3415
|
+
});
|
|
3416
|
+
}
|
|
3281
3417
|
}
|
|
3282
3418
|
}
|
|
3283
3419
|
}
|
|
@@ -3652,9 +3788,62 @@ var MigrationGenerator = class {
|
|
|
3652
3788
|
renameColumn(from, to) {
|
|
3653
3789
|
this.writer.writeLine(`table.renameColumn('${from}', '${to}');`);
|
|
3654
3790
|
}
|
|
3655
|
-
|
|
3791
|
+
getConstraintName(model, entry, index) {
|
|
3656
3792
|
return `${model.name}_${entry.name}_${entry.kind}_${index}`;
|
|
3657
3793
|
}
|
|
3794
|
+
static SQL_KEYWORDS = /* @__PURE__ */ new Set([
|
|
3795
|
+
"and",
|
|
3796
|
+
"or",
|
|
3797
|
+
"not",
|
|
3798
|
+
"in",
|
|
3799
|
+
"is",
|
|
3800
|
+
"null",
|
|
3801
|
+
"true",
|
|
3802
|
+
"false",
|
|
3803
|
+
"between",
|
|
3804
|
+
"like",
|
|
3805
|
+
"exists",
|
|
3806
|
+
"all",
|
|
3807
|
+
"any",
|
|
3808
|
+
"asc",
|
|
3809
|
+
"desc",
|
|
3810
|
+
"with",
|
|
3811
|
+
"using",
|
|
3812
|
+
"as",
|
|
3813
|
+
"on",
|
|
3814
|
+
"infinity",
|
|
3815
|
+
"extract",
|
|
3816
|
+
"current_date",
|
|
3817
|
+
"current_timestamp"
|
|
3818
|
+
]);
|
|
3819
|
+
static LITERAL_PLACEHOLDER = "\uE000";
|
|
3820
|
+
normalizeSqlIdentifiers(s) {
|
|
3821
|
+
const literals = [];
|
|
3822
|
+
let result = s.replace(/'([^']|'')*'/g, (lit) => {
|
|
3823
|
+
literals.push(lit);
|
|
3824
|
+
return `${_MigrationGenerator.LITERAL_PLACEHOLDER}${literals.length - 1}${_MigrationGenerator.LITERAL_PLACEHOLDER}`;
|
|
3825
|
+
});
|
|
3826
|
+
result = result.replace(/"([^"]*)"/g, (_, ident) => `"${ident.toLowerCase()}"`);
|
|
3827
|
+
result = result.replace(
|
|
3828
|
+
/\b([a-zA-Z_][a-zA-Z0-9_]*)\b(?!\s*\()/g,
|
|
3829
|
+
(match) => _MigrationGenerator.SQL_KEYWORDS.has(match.toLowerCase()) ? match : `"${match.toLowerCase()}"`
|
|
3830
|
+
);
|
|
3831
|
+
for (let i = 0; i < literals.length; i++) {
|
|
3832
|
+
result = result.replace(
|
|
3833
|
+
new RegExp(`${_MigrationGenerator.LITERAL_PLACEHOLDER}${i}${_MigrationGenerator.LITERAL_PLACEHOLDER}`, "g"),
|
|
3834
|
+
literals[i]
|
|
3835
|
+
);
|
|
3836
|
+
}
|
|
3837
|
+
return result;
|
|
3838
|
+
}
|
|
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
|
+
}
|
|
3658
3847
|
normalizeCheckExpression(expr) {
|
|
3659
3848
|
let normalized = expr.replace(/\s+/g, " ").trim();
|
|
3660
3849
|
while (this.isWrappedByOuterParentheses(normalized)) {
|
|
@@ -3790,10 +3979,11 @@ var MigrationGenerator = class {
|
|
|
3790
3979
|
escapeExpressionForRaw(expr) {
|
|
3791
3980
|
return expr.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$");
|
|
3792
3981
|
}
|
|
3793
|
-
addCheckConstraint(table, constraintName, expression) {
|
|
3982
|
+
addCheckConstraint(table, constraintName, expression, deferrable) {
|
|
3794
3983
|
const escaped = this.escapeExpressionForRaw(expression);
|
|
3984
|
+
const deferrableClause = deferrable ? ` DEFERRABLE ${deferrable}` : "";
|
|
3795
3985
|
this.writer.writeLine(
|
|
3796
|
-
`await knex.raw(\`ALTER TABLE "${table}" ADD CONSTRAINT "${constraintName}" CHECK (${escaped})\`);`
|
|
3986
|
+
`await knex.raw(\`ALTER TABLE "${table}" ADD CONSTRAINT "${constraintName}" CHECK (${escaped})${deferrableClause}\`);`
|
|
3797
3987
|
);
|
|
3798
3988
|
this.writer.blankLine();
|
|
3799
3989
|
}
|
|
@@ -3801,6 +3991,43 @@ var MigrationGenerator = class {
|
|
|
3801
3991
|
this.writer.writeLine(`await knex.raw('ALTER TABLE "${table}" DROP CONSTRAINT "${constraintName}"');`);
|
|
3802
3992
|
this.writer.blankLine();
|
|
3803
3993
|
}
|
|
3994
|
+
buildExcludeDef(entry) {
|
|
3995
|
+
const elementsStr = entry.elements.map((el) => "column" in el ? `"${el.column}" WITH ${el.operator}` : `${el.expression} WITH ${el.operator}`).join(", ");
|
|
3996
|
+
const whereClause = entry.where ? ` WHERE (${entry.where})` : "";
|
|
3997
|
+
const deferrableClause = entry.deferrable ? ` DEFERRABLE ${entry.deferrable}` : "";
|
|
3998
|
+
return `EXCLUDE USING ${entry.using} (${elementsStr})${whereClause}${deferrableClause}`;
|
|
3999
|
+
}
|
|
4000
|
+
addExcludeConstraint(table, constraintName, entry) {
|
|
4001
|
+
const def = this.buildExcludeDef(entry);
|
|
4002
|
+
const escaped = this.escapeExpressionForRaw(def);
|
|
4003
|
+
this.writer.writeLine(`await knex.raw(\`ALTER TABLE "${table}" ADD CONSTRAINT "${constraintName}" ${escaped}\`);`);
|
|
4004
|
+
this.writer.blankLine();
|
|
4005
|
+
}
|
|
4006
|
+
dropExcludeConstraint(table, constraintName) {
|
|
4007
|
+
this.writer.writeLine(`await knex.raw('ALTER TABLE "${table}" DROP CONSTRAINT "${constraintName}"');`);
|
|
4008
|
+
this.writer.blankLine();
|
|
4009
|
+
}
|
|
4010
|
+
buildConstraintTriggerDef(table, constraintName, entry) {
|
|
4011
|
+
const eventsStr = entry.events.join(" OR ");
|
|
4012
|
+
const deferrableClause = entry.deferrable ? ` DEFERRABLE ${entry.deferrable}` : "";
|
|
4013
|
+
const argsStr = entry.function.args?.length ? entry.function.args.map((a) => `"${a}"`).join(", ") : "";
|
|
4014
|
+
const executeClause = argsStr ? `EXECUTE FUNCTION ${entry.function.name}(${argsStr})` : `EXECUTE FUNCTION ${entry.function.name}()`;
|
|
4015
|
+
return `CREATE CONSTRAINT TRIGGER "${constraintName}" ${entry.when} ${eventsStr} ON "${table}"${deferrableClause} FOR EACH ${entry.forEach} ${executeClause}`;
|
|
4016
|
+
}
|
|
4017
|
+
addConstraintTrigger(table, constraintName, entry) {
|
|
4018
|
+
const eventsStr = entry.events.join(" OR ");
|
|
4019
|
+
const deferrableClause = entry.deferrable ? ` DEFERRABLE ${entry.deferrable}` : "";
|
|
4020
|
+
const argsStr = entry.function.args?.length ? entry.function.args.map((a) => `"${a}"`).join(", ") : "";
|
|
4021
|
+
const executeClause = argsStr ? `EXECUTE FUNCTION ${entry.function.name}(${argsStr})` : `EXECUTE FUNCTION ${entry.function.name}()`;
|
|
4022
|
+
this.writer.writeLine(
|
|
4023
|
+
`await knex.raw(\`CREATE CONSTRAINT TRIGGER "${constraintName}" ${entry.when} ${eventsStr} ON "${table}"${deferrableClause} FOR EACH ${entry.forEach} ${executeClause}\`);`
|
|
4024
|
+
);
|
|
4025
|
+
this.writer.blankLine();
|
|
4026
|
+
}
|
|
4027
|
+
dropConstraintTrigger(table, constraintName) {
|
|
4028
|
+
this.writer.writeLine(`await knex.raw('ALTER TABLE "${table}" DROP CONSTRAINT "${constraintName}"');`);
|
|
4029
|
+
this.writer.blankLine();
|
|
4030
|
+
}
|
|
3804
4031
|
value(value2) {
|
|
3805
4032
|
if (typeof value2 === "string") {
|
|
3806
4033
|
return `'${value2}'`;
|
|
@@ -4827,5 +5054,6 @@ var printSchemaFromModels = (models) => printSchema((0, import_graphql6.buildAST
|
|
|
4827
5054
|
updateEntity,
|
|
4828
5055
|
updateFunctions,
|
|
4829
5056
|
validateCheckConstraint,
|
|
5057
|
+
validateExcludeConstraint,
|
|
4830
5058
|
value
|
|
4831
5059
|
});
|
|
@@ -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
|
|
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,12 @@ export declare class MigrationGenerator {
|
|
|
30
34
|
private dropTable;
|
|
31
35
|
private renameTable;
|
|
32
36
|
private renameColumn;
|
|
33
|
-
private
|
|
37
|
+
private getConstraintName;
|
|
38
|
+
private static readonly SQL_KEYWORDS;
|
|
39
|
+
private static readonly LITERAL_PLACEHOLDER;
|
|
40
|
+
private normalizeSqlIdentifiers;
|
|
41
|
+
private normalizeExcludeDef;
|
|
42
|
+
private normalizeTriggerDef;
|
|
34
43
|
private normalizeCheckExpression;
|
|
35
44
|
private isWrappedByOuterParentheses;
|
|
36
45
|
private findExistingConstraint;
|
|
@@ -43,6 +52,12 @@ export declare class MigrationGenerator {
|
|
|
43
52
|
private escapeExpressionForRaw;
|
|
44
53
|
private addCheckConstraint;
|
|
45
54
|
private dropCheckConstraint;
|
|
55
|
+
private buildExcludeDef;
|
|
56
|
+
private addExcludeConstraint;
|
|
57
|
+
private dropExcludeConstraint;
|
|
58
|
+
private buildConstraintTriggerDef;
|
|
59
|
+
private addConstraintTrigger;
|
|
60
|
+
private dropConstraintTrigger;
|
|
46
61
|
private value;
|
|
47
62
|
private columnRaw;
|
|
48
63
|
private column;
|