From da3906efa42ee729c11696e2d3255a60ac3fa958 Mon Sep 17 00:00:00 2001 From: Adrien de Peretti Date: Wed, 22 Jan 2025 08:42:06 +0100 Subject: [PATCH] fix: Unique constraint should account for soft deleted records (#11048) FIXES FRMW-2878 **What** Currently, the `one-to-one` unique constraints does not account for deleted record. This prevents from inserting a new record wth the same fk if another one is deleted. **Caveat** `hasOne` with FK option is meant to be a special case, for example a many to one - one to many without defining the other side of the relation. In that case we don't handle this behaviour and keep it as it is --- .changeset/red-mangos-complain.md | 14 ++ .../src/dml/__tests__/entity-builder.spec.ts | 16 +- .../entity-builder/define-relationship.ts | 8 + .../mikro-orm-cli-config-builder.ts | 2 +- .../src/migrations/.snapshot-medusa-cart.json | 18 -- .../src/migrations/Migration20250120115059.ts | 21 ++ .../.snapshot-medusa-fulfillment.json | 183 ++++-------------- .../src/migrations/Migration20250120115002.ts | 40 ++++ .../.snapshot-medusa-notification.json | 42 +++- .../src/migrations/Migration20250120110514.ts | 17 ++ .../migrations/.snapshot-medusa-payment.json | 21 +- .../src/migrations/Migration20250120110552.ts | 19 ++ .../.snapshot-medusa-promotion.json | 26 +-- .../src/migrations/Migration20250120110700.ts | 25 +++ .../migrations/.snapshot-medusa-region.json | 46 ++++- .../src/migrations/Migration20250120110744.ts | 19 ++ .../.snapshot-medusa-stock-location.json | 13 +- .../src/migrations/Migration20250120110820.ts | 17 ++ .../src/models/workflow-execution.ts | 5 + .../.snapshot-medusa-workflows.json | 8 + .../src/migrations/Migration20250120111059.ts | 13 ++ .../src/models/workflow-execution.ts | 5 + 22 files changed, 361 insertions(+), 217 deletions(-) create mode 100644 .changeset/red-mangos-complain.md create mode 100644 packages/modules/cart/src/migrations/Migration20250120115059.ts create mode 100644 packages/modules/fulfillment/src/migrations/Migration20250120115002.ts create mode 100644 packages/modules/notification/src/migrations/Migration20250120110514.ts create mode 100644 packages/modules/payment/src/migrations/Migration20250120110552.ts create mode 100644 packages/modules/promotion/src/migrations/Migration20250120110700.ts create mode 100644 packages/modules/region/src/migrations/Migration20250120110744.ts create mode 100644 packages/modules/stock-location/src/migrations/Migration20250120110820.ts create mode 100644 packages/modules/workflow-engine-redis/src/migrations/Migration20250120111059.ts diff --git a/.changeset/red-mangos-complain.md b/.changeset/red-mangos-complain.md new file mode 100644 index 0000000000..2b2c6ecb77 --- /dev/null +++ b/.changeset/red-mangos-complain.md @@ -0,0 +1,14 @@ +--- +"@medusajs/cart": patch +"@medusajs/fulfillment": patch +"@medusajs/notification": patch +"@medusajs/payment": patch +"@medusajs/promotion": patch +"@medusajs/region": patch +"@medusajs/stock-location": patch +"@medusajs/workflow-engine-inmemory": patch +"@medusajs/workflow-engine-redis": patch +"@medusajs/utils": patch +--- + +fix: Unique constraint should account for soft deleted records diff --git a/packages/core/utils/src/dml/__tests__/entity-builder.spec.ts b/packages/core/utils/src/dml/__tests__/entity-builder.spec.ts index 82eeb8f137..305e2cecbf 100644 --- a/packages/core/utils/src/dml/__tests__/entity-builder.spec.ts +++ b/packages/core/utils/src/dml/__tests__/entity-builder.spec.ts @@ -3094,6 +3094,7 @@ describe("Entity builder", () => { owner: true, kind: "1:1", cascade: ["persist", "soft-remove"], + unique: false, }, user_id: { columnType: "text", @@ -3208,6 +3209,7 @@ describe("Entity builder", () => { name: "email", entity: "Email", fieldName: "email_id", + unique: false, }, email_id: { columnType: "text", @@ -3320,6 +3322,7 @@ describe("Entity builder", () => { entity: "Email", nullable: true, fieldName: "emails_id", + unique: false, }, emails_id: { columnType: "text", @@ -3423,6 +3426,7 @@ describe("Entity builder", () => { entity: "Email", mappedBy: "owner", fieldName: "email_id", + unique: false, }, email_id: { columnType: "text", @@ -3530,6 +3534,7 @@ describe("Entity builder", () => { cascade: ["persist", "soft-remove"], mappedBy: "user", fieldName: "email_id", + unique: false, }, email_id: { columnType: "text", @@ -3707,6 +3712,7 @@ describe("Entity builder", () => { cascade: ["persist", "soft-remove"], mappedBy: "user", fieldName: "email_id", + unique: false, }, email_id: { columnType: "text", @@ -3791,6 +3797,7 @@ describe("Entity builder", () => { kind: "1:1", cascade: ["persist", "soft-remove"], fieldName: "user_id", + unique: false, }, user_id: { columnType: "text", @@ -4125,8 +4132,8 @@ describe("Entity builder", () => { expect(settingMetadata.indexes).toEqual([ { expression: - 'CREATE INDEX IF NOT EXISTS "IDX_setting_user_id" ON "setting" (user_id) WHERE deleted_at IS NULL', - name: "IDX_setting_user_id", + 'CREATE UNIQUE INDEX IF NOT EXISTS "IDX_setting_user_id_unique" ON "setting" (user_id) WHERE deleted_at IS NULL', + name: "IDX_setting_user_id_unique", }, { expression: @@ -4739,6 +4746,7 @@ describe("Entity builder", () => { mappedBy: "email", onDelete: undefined, owner: true, + unique: false, }, user_id: { kind: "scalar", @@ -4953,6 +4961,7 @@ describe("Entity builder", () => { onDelete: undefined, mappedBy: "email", owner: true, + unique: false, }, user_id: { kind: "scalar", @@ -5572,6 +5581,7 @@ describe("Entity builder", () => { mappedBy: "email", onDelete: undefined, owner: true, + unique: false, }, user_id: { kind: "scalar", @@ -5772,6 +5782,7 @@ describe("Entity builder", () => { mappedBy: "email", onDelete: undefined, owner: true, + unique: false, }, user_id: { kind: "scalar", @@ -5995,6 +6006,7 @@ describe("Entity builder", () => { nullable: false, onDelete: undefined, owner: true, + unique: false, }, parent_id: { name: "parent_id", diff --git a/packages/core/utils/src/dml/helpers/entity-builder/define-relationship.ts b/packages/core/utils/src/dml/helpers/entity-builder/define-relationship.ts index 8cf16e0b60..947d724e9c 100644 --- a/packages/core/utils/src/dml/helpers/entity-builder/define-relationship.ts +++ b/packages/core/utils/src/dml/helpers/entity-builder/define-relationship.ts @@ -178,6 +178,7 @@ export function defineHasOneRelationship( */ export function defineHasOneWithFKRelationship( MikroORMEntity: EntityConstructor, + entity: DmlEntity, relationship: RelationshipMetadata, { relatedModelName }: { relatedModelName: string }, cascades: EntityCascades @@ -198,6 +199,7 @@ export function defineHasOneWithFKRelationship( fieldName: foreignKeyName, ...(relationship.nullable ? { nullable: relationship.nullable } : {}), ...(mappedBy ? { mappedBy } : {}), + unique: false, //orphanRemoval: true, } as OneToOneOptions @@ -508,6 +510,10 @@ export function defineBelongsToRelationship( mappedBy: mappedBy, fieldName: foreignKeyName, owner: true, + /** + * If we decide to support non soft deletable then this should be true and the unique index id should be removed + */ + unique: false, // orphanRemoval: true, } @@ -523,6 +529,7 @@ export function defineBelongsToRelationship( { on: [foreignKeyName], where: "deleted_at IS NULL", + unique: true, }, ]) @@ -821,6 +828,7 @@ export function defineRelationship( case "hasOneWithFK": defineHasOneWithFKRelationship( MikroORMEntity, + entity, relationship, relatedEntityInfo, cascades diff --git a/packages/core/utils/src/modules-sdk/mikro-orm-cli-config-builder.ts b/packages/core/utils/src/modules-sdk/mikro-orm-cli-config-builder.ts index d61db08c4c..1dd9defde4 100644 --- a/packages/core/utils/src/modules-sdk/mikro-orm-cli-config-builder.ts +++ b/packages/core/utils/src/modules-sdk/mikro-orm-cli-config-builder.ts @@ -59,7 +59,7 @@ export function defineMikroOrmCliConfig( user: "postgres", password: "", ...(options as any), - entities, + entities: entities.filter(Boolean), migrations: { generator: CustomTsMigrationGenerator, ...options.migrations, diff --git a/packages/modules/cart/src/migrations/.snapshot-medusa-cart.json b/packages/modules/cart/src/migrations/.snapshot-medusa-cart.json index bcdc0b173d..c8b7dde8b3 100644 --- a/packages/modules/cart/src/migrations/.snapshot-medusa-cart.json +++ b/packages/modules/cart/src/migrations/.snapshot-medusa-cart.json @@ -309,24 +309,6 @@ "name": "cart", "schema": "public", "indexes": [ - { - "columnNames": [ - "shipping_address_id" - ], - "composite": false, - "keyName": "cart_shipping_address_id_unique", - "primary": false, - "unique": true - }, - { - "columnNames": [ - "billing_address_id" - ], - "composite": false, - "keyName": "cart_billing_address_id_unique", - "primary": false, - "unique": true - }, { "keyName": "IDX_cart_deleted_at", "columnNames": [], diff --git a/packages/modules/cart/src/migrations/Migration20250120115059.ts b/packages/modules/cart/src/migrations/Migration20250120115059.ts new file mode 100644 index 0000000000..e56d705a40 --- /dev/null +++ b/packages/modules/cart/src/migrations/Migration20250120115059.ts @@ -0,0 +1,21 @@ +import { Migration } from "@mikro-orm/migrations" + +export class Migration20250120115059 extends Migration { + async up(): Promise { + this.addSql( + 'alter table if exists "cart" drop constraint if exists "cart_shipping_address_id_unique";' + ) + this.addSql( + 'alter table if exists "cart" drop constraint if exists "cart_billing_address_id_unique";' + ) + } + + async down(): Promise { + this.addSql( + 'alter table if exists "cart" add constraint "cart_shipping_address_id_unique" unique ("shipping_address_id");' + ) + this.addSql( + 'alter table if exists "cart" add constraint "cart_billing_address_id_unique" unique ("billing_address_id");' + ) + } +} diff --git a/packages/modules/fulfillment/src/migrations/.snapshot-medusa-fulfillment.json b/packages/modules/fulfillment/src/migrations/.snapshot-medusa-fulfillment.json index 0c2078c51b..9e67680c91 100644 --- a/packages/modules/fulfillment/src/migrations/.snapshot-medusa-fulfillment.json +++ b/packages/modules/fulfillment/src/migrations/.snapshot-medusa-fulfillment.json @@ -1,7 +1,5 @@ { - "namespaces": [ - "public" - ], + "namespaces": ["public"], "name": "public", "tables": [ { @@ -161,9 +159,7 @@ }, { "keyName": "fulfillment_address_pkey", - "columnNames": [ - "id" - ], + "columnNames": ["id"], "composite": false, "constraint": true, "primary": true, @@ -242,9 +238,7 @@ }, { "keyName": "fulfillment_provider_pkey", - "columnNames": [ - "id" - ], + "columnNames": ["id"], "composite": false, "constraint": true, "primary": true, @@ -349,9 +343,7 @@ }, { "keyName": "fulfillment_set_pkey", - "columnNames": [ - "id" - ], + "columnNames": ["id"], "composite": false, "constraint": true, "primary": true, @@ -465,9 +457,7 @@ }, { "keyName": "service_zone_pkey", - "columnNames": [ - "id" - ], + "columnNames": ["id"], "composite": false, "constraint": true, "primary": true, @@ -478,13 +468,9 @@ "foreignKeys": { "service_zone_fulfillment_set_id_foreign": { "constraintName": "service_zone_fulfillment_set_id_foreign", - "columnNames": [ - "fulfillment_set_id" - ], + "columnNames": ["fulfillment_set_id"], "localTableName": "public.service_zone", - "referencedColumnNames": [ - "id" - ], + "referencedColumnNames": ["id"], "referencedTableName": "public.fulfillment_set", "deleteRule": "cascade", "updateRule": "cascade" @@ -511,12 +497,7 @@ "primary": false, "nullable": false, "default": "'country'", - "enumItems": [ - "country", - "province", - "city", - "zip" - ], + "enumItems": ["country", "province", "city", "zip"], "mappedType": "enum" }, "country_code": { @@ -656,9 +637,7 @@ }, { "keyName": "geo_zone_pkey", - "columnNames": [ - "id" - ], + "columnNames": ["id"], "composite": false, "constraint": true, "primary": true, @@ -669,13 +648,9 @@ "foreignKeys": { "geo_zone_service_zone_id_foreign": { "constraintName": "geo_zone_service_zone_id_foreign", - "columnNames": [ - "service_zone_id" - ], + "columnNames": ["service_zone_id"], "localTableName": "public.geo_zone", - "referencedColumnNames": [ - "id" - ], + "referencedColumnNames": ["id"], "referencedTableName": "public.service_zone", "deleteRule": "cascade", "updateRule": "cascade" @@ -768,9 +743,7 @@ }, { "keyName": "shipping_option_type_pkey", - "columnNames": [ - "id" - ], + "columnNames": ["id"], "composite": false, "constraint": true, "primary": true, @@ -875,9 +848,7 @@ }, { "keyName": "shipping_profile_pkey", - "columnNames": [ - "id" - ], + "columnNames": ["id"], "composite": false, "constraint": true, "primary": true, @@ -916,10 +887,7 @@ "primary": false, "nullable": false, "default": "'flat'", - "enumItems": [ - "calculated", - "flat" - ], + "enumItems": ["calculated", "flat"], "mappedType": "enum" }, "data": { @@ -1013,9 +981,7 @@ "schema": "public", "indexes": [ { - "columnNames": [ - "shipping_option_type_id" - ], + "columnNames": ["shipping_option_type_id"], "composite": false, "keyName": "shipping_option_shipping_option_type_id_unique", "constraint": true, @@ -1060,9 +1026,7 @@ }, { "keyName": "shipping_option_pkey", - "columnNames": [ - "id" - ], + "columnNames": ["id"], "composite": false, "constraint": true, "primary": true, @@ -1073,52 +1037,36 @@ "foreignKeys": { "shipping_option_service_zone_id_foreign": { "constraintName": "shipping_option_service_zone_id_foreign", - "columnNames": [ - "service_zone_id" - ], + "columnNames": ["service_zone_id"], "localTableName": "public.shipping_option", - "referencedColumnNames": [ - "id" - ], + "referencedColumnNames": ["id"], "referencedTableName": "public.service_zone", "deleteRule": "cascade", "updateRule": "cascade" }, "shipping_option_shipping_profile_id_foreign": { "constraintName": "shipping_option_shipping_profile_id_foreign", - "columnNames": [ - "shipping_profile_id" - ], + "columnNames": ["shipping_profile_id"], "localTableName": "public.shipping_option", - "referencedColumnNames": [ - "id" - ], + "referencedColumnNames": ["id"], "referencedTableName": "public.shipping_profile", "deleteRule": "set null", "updateRule": "cascade" }, "shipping_option_provider_id_foreign": { "constraintName": "shipping_option_provider_id_foreign", - "columnNames": [ - "provider_id" - ], + "columnNames": ["provider_id"], "localTableName": "public.shipping_option", - "referencedColumnNames": [ - "id" - ], + "referencedColumnNames": ["id"], "referencedTableName": "public.fulfillment_provider", "deleteRule": "set null", "updateRule": "cascade" }, "shipping_option_shipping_option_type_id_foreign": { "constraintName": "shipping_option_shipping_option_type_id_foreign", - "columnNames": [ - "shipping_option_type_id" - ], + "columnNames": ["shipping_option_type_id"], "localTableName": "public.shipping_option", - "referencedColumnNames": [ - "id" - ], + "referencedColumnNames": ["id"], "referencedTableName": "public.shipping_option_type", "updateRule": "cascade" } @@ -1152,16 +1100,7 @@ "autoincrement": false, "primary": false, "nullable": false, - "enumItems": [ - "in", - "eq", - "ne", - "gt", - "gte", - "lt", - "lte", - "nin" - ], + "enumItems": ["in", "eq", "ne", "gt", "gte", "lt", "lte", "nin"], "mappedType": "enum" }, "value": { @@ -1238,9 +1177,7 @@ }, { "keyName": "shipping_option_rule_pkey", - "columnNames": [ - "id" - ], + "columnNames": ["id"], "composite": false, "constraint": true, "primary": true, @@ -1251,13 +1188,9 @@ "foreignKeys": { "shipping_option_rule_shipping_option_id_foreign": { "constraintName": "shipping_option_rule_shipping_option_id_foreign", - "columnNames": [ - "shipping_option_id" - ], + "columnNames": ["shipping_option_id"], "localTableName": "public.shipping_option_rule", - "referencedColumnNames": [ - "id" - ], + "referencedColumnNames": ["id"], "referencedTableName": "public.shipping_option", "deleteRule": "cascade", "updateRule": "cascade" @@ -1435,9 +1368,7 @@ "schema": "public", "indexes": [ { - "columnNames": [ - "provider_id" - ], + "columnNames": ["provider_id"], "composite": false, "keyName": "fulfillment_provider_id_unique", "constraint": true, @@ -1445,9 +1376,7 @@ "unique": true }, { - "columnNames": [ - "delivery_address_id" - ], + "columnNames": ["delivery_address_id"], "composite": false, "keyName": "fulfillment_delivery_address_id_unique", "constraint": true, @@ -1483,9 +1412,7 @@ }, { "keyName": "fulfillment_pkey", - "columnNames": [ - "id" - ], + "columnNames": ["id"], "composite": false, "constraint": true, "primary": true, @@ -1496,39 +1423,27 @@ "foreignKeys": { "fulfillment_provider_id_foreign": { "constraintName": "fulfillment_provider_id_foreign", - "columnNames": [ - "provider_id" - ], + "columnNames": ["provider_id"], "localTableName": "public.fulfillment", - "referencedColumnNames": [ - "id" - ], + "referencedColumnNames": ["id"], "referencedTableName": "public.fulfillment_provider", "deleteRule": "set null", "updateRule": "cascade" }, "fulfillment_shipping_option_id_foreign": { "constraintName": "fulfillment_shipping_option_id_foreign", - "columnNames": [ - "shipping_option_id" - ], + "columnNames": ["shipping_option_id"], "localTableName": "public.fulfillment", - "referencedColumnNames": [ - "id" - ], + "referencedColumnNames": ["id"], "referencedTableName": "public.shipping_option", "deleteRule": "set null", "updateRule": "cascade" }, "fulfillment_delivery_address_id_foreign": { "constraintName": "fulfillment_delivery_address_id_foreign", - "columnNames": [ - "delivery_address_id" - ], + "columnNames": ["delivery_address_id"], "localTableName": "public.fulfillment", - "referencedColumnNames": [ - "id" - ], + "referencedColumnNames": ["id"], "referencedTableName": "public.fulfillment_address", "deleteRule": "set null", "updateRule": "cascade" @@ -1639,9 +1554,7 @@ }, { "keyName": "fulfillment_label_pkey", - "columnNames": [ - "id" - ], + "columnNames": ["id"], "composite": false, "constraint": true, "primary": true, @@ -1652,13 +1565,9 @@ "foreignKeys": { "fulfillment_label_fulfillment_id_foreign": { "constraintName": "fulfillment_label_fulfillment_id_foreign", - "columnNames": [ - "fulfillment_id" - ], + "columnNames": ["fulfillment_id"], "localTableName": "public.fulfillment_label", - "referencedColumnNames": [ - "id" - ], + "referencedColumnNames": ["id"], "referencedTableName": "public.fulfillment", "deleteRule": "cascade", "updateRule": "cascade" @@ -1823,9 +1732,7 @@ }, { "keyName": "fulfillment_item_pkey", - "columnNames": [ - "id" - ], + "columnNames": ["id"], "composite": false, "constraint": true, "primary": true, @@ -1836,13 +1743,9 @@ "foreignKeys": { "fulfillment_item_fulfillment_id_foreign": { "constraintName": "fulfillment_item_fulfillment_id_foreign", - "columnNames": [ - "fulfillment_id" - ], + "columnNames": ["fulfillment_id"], "localTableName": "public.fulfillment_item", - "referencedColumnNames": [ - "id" - ], + "referencedColumnNames": ["id"], "referencedTableName": "public.fulfillment", "deleteRule": "cascade", "updateRule": "cascade" diff --git a/packages/modules/fulfillment/src/migrations/Migration20250120115002.ts b/packages/modules/fulfillment/src/migrations/Migration20250120115002.ts new file mode 100644 index 0000000000..c52829cecc --- /dev/null +++ b/packages/modules/fulfillment/src/migrations/Migration20250120115002.ts @@ -0,0 +1,40 @@ +import { Migration } from "@mikro-orm/migrations" + +export class Migration20250120115002 extends Migration { + async up(): Promise { + this.addSql( + 'alter table if exists "shipping_option" add constraint "shipping_option_provider_id_foreign" foreign key ("provider_id") references "fulfillment_provider" ("id") on update cascade on delete set null;' + ) + this.addSql( + 'alter table if exists "shipping_option" add constraint "shipping_option_shipping_option_type_id_foreign" foreign key ("shipping_option_type_id") references "shipping_option_type" ("id") on update cascade;' + ) + this.addSql( + 'CREATE INDEX IF NOT EXISTS "IDX_shipping_option_provider_id" ON "shipping_option" (provider_id) WHERE deleted_at IS NULL;' + ) + + this.addSql( + 'alter table if exists "fulfillment" add constraint "fulfillment_provider_id_foreign" foreign key ("provider_id") references "fulfillment_provider" ("id") on update cascade on delete set null;' + ) + this.addSql( + 'alter table if exists "fulfillment" add constraint "fulfillment_delivery_address_id_foreign" foreign key ("delivery_address_id") references "fulfillment_address" ("id") on update cascade on delete set null;' + ) + } + + async down(): Promise { + this.addSql( + 'alter table if exists "shipping_option" drop constraint if exists "shipping_option_provider_id_foreign";' + ) + this.addSql( + 'alter table if exists "shipping_option" drop constraint if exists "shipping_option_shipping_option_type_id_foreign";' + ) + + this.addSql( + 'alter table if exists "fulfillment" drop constraint if exists "fulfillment_provider_id_foreign";' + ) + this.addSql( + 'alter table if exists "fulfillment" drop constraint if exists "fulfillment_delivery_address_id_foreign";' + ) + + this.addSql('drop index if exists "IDX_shipping_option_provider_id";') + } +} diff --git a/packages/modules/notification/src/migrations/.snapshot-medusa-notification.json b/packages/modules/notification/src/migrations/.snapshot-medusa-notification.json index 00f8a26dd7..3a6ea7b7f7 100644 --- a/packages/modules/notification/src/migrations/.snapshot-medusa-notification.json +++ b/packages/modules/notification/src/migrations/.snapshot-medusa-notification.json @@ -1,5 +1,7 @@ { - "namespaces": ["public"], + "namespaces": [ + "public" + ], "name": "public", "tables": [ { @@ -87,9 +89,19 @@ "name": "notification_provider", "schema": "public", "indexes": [ + { + "keyName": "IDX_notification_provider_deleted_at", + "columnNames": [], + "composite": false, + "primary": false, + "unique": false, + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_notification_provider_deleted_at\" ON \"notification_provider\" (deleted_at) WHERE deleted_at IS NULL" + }, { "keyName": "notification_provider_pkey", - "columnNames": ["id"], + "columnNames": [ + "id" + ], "composite": false, "primary": true, "unique": true @@ -216,7 +228,11 @@ "primary": false, "nullable": false, "default": "'pending'", - "enumItems": ["pending", "success", "failure"], + "enumItems": [ + "pending", + "success", + "failure" + ], "mappedType": "enum" }, "provider_id": { @@ -288,9 +304,19 @@ "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_notification_provider_id\" ON \"notification\" (provider_id) WHERE deleted_at IS NULL" }, + { + "keyName": "IDX_notification_deleted_at", + "columnNames": [], + "composite": false, + "primary": false, + "unique": false, + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_notification_deleted_at\" ON \"notification\" (deleted_at) WHERE deleted_at IS NULL" + }, { "keyName": "notification_pkey", - "columnNames": ["id"], + "columnNames": [ + "id" + ], "composite": false, "primary": true, "unique": true @@ -300,9 +326,13 @@ "foreignKeys": { "notification_provider_id_foreign": { "constraintName": "notification_provider_id_foreign", - "columnNames": ["provider_id"], + "columnNames": [ + "provider_id" + ], "localTableName": "public.notification", - "referencedColumnNames": ["id"], + "referencedColumnNames": [ + "id" + ], "referencedTableName": "public.notification_provider", "deleteRule": "set null", "updateRule": "cascade" diff --git a/packages/modules/notification/src/migrations/Migration20250120110514.ts b/packages/modules/notification/src/migrations/Migration20250120110514.ts new file mode 100644 index 0000000000..6d9f81a5e8 --- /dev/null +++ b/packages/modules/notification/src/migrations/Migration20250120110514.ts @@ -0,0 +1,17 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20250120110514 extends Migration { + + async up(): Promise { + this.addSql('CREATE INDEX IF NOT EXISTS "IDX_notification_provider_deleted_at" ON "notification_provider" (deleted_at) WHERE deleted_at IS NULL;'); + + this.addSql('CREATE INDEX IF NOT EXISTS "IDX_notification_deleted_at" ON "notification" (deleted_at) WHERE deleted_at IS NULL;'); + } + + async down(): Promise { + this.addSql('drop index if exists "IDX_notification_provider_deleted_at";'); + + this.addSql('drop index if exists "IDX_notification_deleted_at";'); + } + +} diff --git a/packages/modules/payment/src/migrations/.snapshot-medusa-payment.json b/packages/modules/payment/src/migrations/.snapshot-medusa-payment.json index 2f62d60d1a..aeb585024a 100644 --- a/packages/modules/payment/src/migrations/.snapshot-medusa-payment.json +++ b/packages/modules/payment/src/migrations/.snapshot-medusa-payment.json @@ -784,15 +784,6 @@ "name": "payment", "schema": "public", "indexes": [ - { - "columnNames": [ - "payment_session_id" - ], - "composite": false, - "keyName": "payment_payment_session_id_unique", - "primary": false, - "unique": true - }, { "keyName": "IDX_payment_payment_collection_id", "columnNames": [], @@ -802,12 +793,12 @@ "expression": "CREATE INDEX IF NOT EXISTS \"IDX_payment_payment_collection_id\" ON \"payment\" (payment_collection_id) WHERE deleted_at IS NULL" }, { - "keyName": "IDX_payment_payment_session_id", + "keyName": "IDX_payment_payment_session_id_unique", "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_payment_payment_session_id\" ON \"payment\" (payment_session_id) WHERE deleted_at IS NULL" + "expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_payment_payment_session_id_unique\" ON \"payment\" (payment_session_id) WHERE deleted_at IS NULL" }, { "keyName": "IDX_payment_deleted_at", @@ -825,6 +816,14 @@ "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_payment_provider_id\" ON \"payment\" (provider_id) WHERE deleted_at IS NULL" }, + { + "keyName": "IDX_payment_payment_session_id", + "columnNames": [], + "composite": false, + "primary": false, + "unique": false, + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_payment_payment_session_id\" ON \"payment\" (payment_session_id) WHERE deleted_at IS NULL" + }, { "keyName": "payment_pkey", "columnNames": [ diff --git a/packages/modules/payment/src/migrations/Migration20250120110552.ts b/packages/modules/payment/src/migrations/Migration20250120110552.ts new file mode 100644 index 0000000000..0bde8a465a --- /dev/null +++ b/packages/modules/payment/src/migrations/Migration20250120110552.ts @@ -0,0 +1,19 @@ +import { Migration } from "@mikro-orm/migrations" + +export class Migration20250120110552 extends Migration { + async up(): Promise { + this.addSql( + 'alter table if exists "payment" drop constraint if exists "payment_payment_session_id_unique";' + ) + this.addSql( + 'CREATE UNIQUE INDEX IF NOT EXISTS "IDX_payment_payment_session_id_unique" ON "payment" (payment_session_id) WHERE deleted_at IS NULL;' + ) + } + + async down(): Promise { + this.addSql('drop index if exists "IDX_payment_payment_session_id_unique";') + this.addSql( + 'alter table if exists "payment" add constraint "payment_payment_session_id_unique" unique ("payment_session_id");' + ) + } +} diff --git a/packages/modules/promotion/src/migrations/.snapshot-medusa-promotion.json b/packages/modules/promotion/src/migrations/.snapshot-medusa-promotion.json index 406f1c5108..90fb4fddd7 100644 --- a/packages/modules/promotion/src/migrations/.snapshot-medusa-promotion.json +++ b/packages/modules/promotion/src/migrations/.snapshot-medusa-promotion.json @@ -242,15 +242,6 @@ "name": "promotion_campaign_budget", "schema": "public", "indexes": [ - { - "columnNames": [ - "campaign_id" - ], - "composite": false, - "keyName": "promotion_campaign_budget_campaign_id_unique", - "primary": false, - "unique": true - }, { "keyName": "IDX_campaign_budget_type", "columnNames": [], @@ -260,12 +251,12 @@ "expression": "CREATE INDEX IF NOT EXISTS \"IDX_campaign_budget_type\" ON \"promotion_campaign_budget\" (type) WHERE deleted_at IS NULL" }, { - "keyName": "IDX_promotion_campaign_budget_campaign_id", + "keyName": "IDX_promotion_campaign_budget_campaign_id_unique", "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_promotion_campaign_budget_campaign_id\" ON \"promotion_campaign_budget\" (campaign_id) WHERE deleted_at IS NULL" + "expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_promotion_campaign_budget_campaign_id_unique\" ON \"promotion_campaign_budget\" (campaign_id) WHERE deleted_at IS NULL" }, { "keyName": "IDX_promotion_campaign_budget_deleted_at", @@ -630,15 +621,6 @@ "name": "promotion_application_method", "schema": "public", "indexes": [ - { - "columnNames": [ - "promotion_id" - ], - "composite": false, - "keyName": "promotion_application_method_promotion_id_unique", - "primary": false, - "unique": true - }, { "keyName": "IDX_application_method_type", "columnNames": [], @@ -664,12 +646,12 @@ "expression": "CREATE INDEX IF NOT EXISTS \"IDX_application_method_allocation\" ON \"promotion_application_method\" (allocation) WHERE deleted_at IS NULL" }, { - "keyName": "IDX_promotion_application_method_promotion_id", + "keyName": "IDX_promotion_application_method_promotion_id_unique", "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_promotion_application_method_promotion_id\" ON \"promotion_application_method\" (promotion_id) WHERE deleted_at IS NULL" + "expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_promotion_application_method_promotion_id_unique\" ON \"promotion_application_method\" (promotion_id) WHERE deleted_at IS NULL" }, { "keyName": "IDX_promotion_application_method_deleted_at", diff --git a/packages/modules/promotion/src/migrations/Migration20250120110700.ts b/packages/modules/promotion/src/migrations/Migration20250120110700.ts new file mode 100644 index 0000000000..a7b0d4f8b8 --- /dev/null +++ b/packages/modules/promotion/src/migrations/Migration20250120110700.ts @@ -0,0 +1,25 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20250120110700 extends Migration { + + async up(): Promise { + this.addSql('alter table if exists "promotion_campaign_budget" drop constraint if exists "promotion_campaign_budget_campaign_id_unique";'); + this.addSql('drop index if exists "IDX_promotion_campaign_budget_campaign_id";'); + this.addSql('CREATE UNIQUE INDEX IF NOT EXISTS "IDX_promotion_campaign_budget_campaign_id_unique" ON "promotion_campaign_budget" (campaign_id) WHERE deleted_at IS NULL;'); + + this.addSql('alter table if exists "promotion_application_method" drop constraint if exists "promotion_application_method_promotion_id_unique";'); + this.addSql('drop index if exists "IDX_promotion_application_method_promotion_id";'); + this.addSql('CREATE UNIQUE INDEX IF NOT EXISTS "IDX_promotion_application_method_promotion_id_unique" ON "promotion_application_method" (promotion_id) WHERE deleted_at IS NULL;'); + } + + async down(): Promise { + this.addSql('drop index if exists "IDX_promotion_campaign_budget_campaign_id_unique";'); + this.addSql('alter table if exists "promotion_campaign_budget" add constraint "promotion_campaign_budget_campaign_id_unique" unique ("campaign_id");'); + this.addSql('CREATE INDEX IF NOT EXISTS "IDX_promotion_campaign_budget_campaign_id" ON "promotion_campaign_budget" (campaign_id) WHERE deleted_at IS NULL;'); + + this.addSql('drop index if exists "IDX_promotion_application_method_promotion_id_unique";'); + this.addSql('alter table if exists "promotion_application_method" add constraint "promotion_application_method_promotion_id_unique" unique ("promotion_id");'); + this.addSql('CREATE INDEX IF NOT EXISTS "IDX_promotion_application_method_promotion_id" ON "promotion_application_method" (promotion_id) WHERE deleted_at IS NULL;'); + } + +} diff --git a/packages/modules/region/src/migrations/.snapshot-medusa-region.json b/packages/modules/region/src/migrations/.snapshot-medusa-region.json index 48042b3afc..3e1dd0867b 100644 --- a/packages/modules/region/src/migrations/.snapshot-medusa-region.json +++ b/packages/modules/region/src/migrations/.snapshot-medusa-region.json @@ -1,5 +1,7 @@ { - "namespaces": ["public"], + "namespaces": [ + "public" + ], "name": "public", "tables": [ { @@ -86,9 +88,19 @@ "name": "region", "schema": "public", "indexes": [ + { + "keyName": "IDX_region_deleted_at", + "columnNames": [], + "composite": false, + "primary": false, + "unique": false, + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_region_deleted_at\" ON \"region\" (deleted_at) WHERE deleted_at IS NULL" + }, { "keyName": "region_pkey", - "columnNames": ["id"], + "columnNames": [ + "id" + ], "composite": false, "primary": true, "unique": true @@ -198,17 +210,35 @@ "name": "region_country", "schema": "public", "indexes": [ + { + "keyName": "IDX_region_country_region_id", + "columnNames": [], + "composite": false, + "primary": false, + "unique": false, + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_region_country_region_id\" ON \"region_country\" (region_id) WHERE deleted_at IS NULL" + }, + { + "keyName": "IDX_region_country_deleted_at", + "columnNames": [], + "composite": false, + "primary": false, + "unique": false, + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_region_country_deleted_at\" ON \"region_country\" (deleted_at) WHERE deleted_at IS NULL" + }, { "keyName": "IDX_region_country_region_id_iso_2_unique", "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_region_country_region_id_iso_2_unique\" ON \"region_country\" (region_id, iso_2)" + "expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_region_country_region_id_iso_2_unique\" ON \"region_country\" (region_id, iso_2) WHERE deleted_at IS NULL" }, { "keyName": "region_country_pkey", - "columnNames": ["iso_2"], + "columnNames": [ + "iso_2" + ], "composite": false, "primary": true, "unique": true @@ -218,9 +248,13 @@ "foreignKeys": { "region_country_region_id_foreign": { "constraintName": "region_country_region_id_foreign", - "columnNames": ["region_id"], + "columnNames": [ + "region_id" + ], "localTableName": "public.region_country", - "referencedColumnNames": ["id"], + "referencedColumnNames": [ + "id" + ], "referencedTableName": "public.region", "deleteRule": "set null", "updateRule": "cascade" diff --git a/packages/modules/region/src/migrations/Migration20250120110744.ts b/packages/modules/region/src/migrations/Migration20250120110744.ts new file mode 100644 index 0000000000..14f8678f48 --- /dev/null +++ b/packages/modules/region/src/migrations/Migration20250120110744.ts @@ -0,0 +1,19 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20250120110744 extends Migration { + + async up(): Promise { + this.addSql('CREATE INDEX IF NOT EXISTS "IDX_region_deleted_at" ON "region" (deleted_at) WHERE deleted_at IS NULL;'); + + this.addSql('CREATE INDEX IF NOT EXISTS "IDX_region_country_region_id" ON "region_country" (region_id) WHERE deleted_at IS NULL;'); + this.addSql('CREATE INDEX IF NOT EXISTS "IDX_region_country_deleted_at" ON "region_country" (deleted_at) WHERE deleted_at IS NULL;'); + } + + async down(): Promise { + this.addSql('drop index if exists "IDX_region_deleted_at";'); + + this.addSql('drop index if exists "IDX_region_country_region_id";'); + this.addSql('drop index if exists "IDX_region_country_deleted_at";'); + } + +} diff --git a/packages/modules/stock-location/src/migrations/.snapshot-medusa-stock-location.json b/packages/modules/stock-location/src/migrations/.snapshot-medusa-stock-location.json index 1f7dce4f46..ee2bd42725 100644 --- a/packages/modules/stock-location/src/migrations/.snapshot-medusa-stock-location.json +++ b/packages/modules/stock-location/src/migrations/.snapshot-medusa-stock-location.json @@ -228,21 +228,12 @@ "schema": "public", "indexes": [ { - "columnNames": [ - "address_id" - ], - "composite": false, - "keyName": "stock_location_address_id_unique", - "primary": false, - "unique": true - }, - { - "keyName": "IDX_stock_location_address_id", + "keyName": "IDX_stock_location_address_id_unique", "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_stock_location_address_id\" ON \"stock_location\" (address_id) WHERE deleted_at IS NULL" + "expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_stock_location_address_id_unique\" ON \"stock_location\" (address_id) WHERE deleted_at IS NULL" }, { "keyName": "IDX_stock_location_deleted_at", diff --git a/packages/modules/stock-location/src/migrations/Migration20250120110820.ts b/packages/modules/stock-location/src/migrations/Migration20250120110820.ts new file mode 100644 index 0000000000..f6d69e83a1 --- /dev/null +++ b/packages/modules/stock-location/src/migrations/Migration20250120110820.ts @@ -0,0 +1,17 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20250120110820 extends Migration { + + async up(): Promise { + this.addSql('alter table if exists "stock_location" drop constraint if exists "stock_location_address_id_unique";'); + this.addSql('drop index if exists "IDX_stock_location_address_id";'); + this.addSql('CREATE UNIQUE INDEX IF NOT EXISTS "IDX_stock_location_address_id_unique" ON "stock_location" (address_id) WHERE deleted_at IS NULL;'); + } + + async down(): Promise { + this.addSql('drop index if exists "IDX_stock_location_address_id_unique";'); + this.addSql('alter table if exists "stock_location" add constraint "stock_location_address_id_unique" unique ("address_id");'); + this.addSql('CREATE INDEX IF NOT EXISTS "IDX_stock_location_address_id" ON "stock_location" (address_id) WHERE deleted_at IS NULL;'); + } + +} diff --git a/packages/modules/workflow-engine-inmemory/src/models/workflow-execution.ts b/packages/modules/workflow-engine-inmemory/src/models/workflow-execution.ts index c41bc8936e..8557e77ae6 100644 --- a/packages/modules/workflow-engine-inmemory/src/models/workflow-execution.ts +++ b/packages/modules/workflow-engine-inmemory/src/models/workflow-execution.ts @@ -23,6 +23,11 @@ export const WorkflowExecution = model on: ["transaction_id"], where: "deleted_at IS NULL", }, + { + on: ["workflow_id", "transaction_id"], + unique: true, + where: "deleted_at IS NULL", + }, { on: ["state"], where: "deleted_at IS NULL", diff --git a/packages/modules/workflow-engine-redis/src/migrations/.snapshot-medusa-workflows.json b/packages/modules/workflow-engine-redis/src/migrations/.snapshot-medusa-workflows.json index 66a9a97c21..c94f3039a5 100644 --- a/packages/modules/workflow-engine-redis/src/migrations/.snapshot-medusa-workflows.json +++ b/packages/modules/workflow-engine-redis/src/migrations/.snapshot-medusa-workflows.json @@ -137,6 +137,14 @@ "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_workflow_execution_transaction_id\" ON \"workflow_execution\" (transaction_id) WHERE deleted_at IS NULL" }, + { + "keyName": "IDX_workflow_execution_workflow_id_transaction_id_unique", + "columnNames": [], + "composite": false, + "primary": false, + "unique": false, + "expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_workflow_execution_workflow_id_transaction_id_unique\" ON \"workflow_execution\" (workflow_id, transaction_id) WHERE deleted_at IS NULL" + }, { "keyName": "IDX_workflow_execution_state", "columnNames": [], diff --git a/packages/modules/workflow-engine-redis/src/migrations/Migration20250120111059.ts b/packages/modules/workflow-engine-redis/src/migrations/Migration20250120111059.ts new file mode 100644 index 0000000000..3c2d50b462 --- /dev/null +++ b/packages/modules/workflow-engine-redis/src/migrations/Migration20250120111059.ts @@ -0,0 +1,13 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20250120111059 extends Migration { + + async up(): Promise { + this.addSql('CREATE UNIQUE INDEX IF NOT EXISTS "IDX_workflow_execution_workflow_id_transaction_id_unique" ON "workflow_execution" (workflow_id, transaction_id) WHERE deleted_at IS NULL;'); + } + + async down(): Promise { + this.addSql('drop index if exists "IDX_workflow_execution_workflow_id_transaction_id_unique";'); + } + +} diff --git a/packages/modules/workflow-engine-redis/src/models/workflow-execution.ts b/packages/modules/workflow-engine-redis/src/models/workflow-execution.ts index c41bc8936e..8557e77ae6 100644 --- a/packages/modules/workflow-engine-redis/src/models/workflow-execution.ts +++ b/packages/modules/workflow-engine-redis/src/models/workflow-execution.ts @@ -23,6 +23,11 @@ export const WorkflowExecution = model on: ["transaction_id"], where: "deleted_at IS NULL", }, + { + on: ["workflow_id", "transaction_id"], + unique: true, + where: "deleted_at IS NULL", + }, { on: ["state"], where: "deleted_at IS NULL",