Fix: Add unique constraint for policy_settings upsert
All checks were successful
Trigger Cloudarix Deploy / call-webhook (push) Successful in 1s
All checks were successful
Trigger Cloudarix Deploy / call-webhook (push) Successful in 1s
This commit is contained in:
parent
bd7758191e
commit
8b36902767
43
fix-constraint.ts
Normal file
43
fix-constraint.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { drizzle } from 'drizzle-orm/node-postgres';
|
||||
import { Pool } from 'pg';
|
||||
import { sql } from 'drizzle-orm';
|
||||
|
||||
async function fixConstraint() {
|
||||
const dbUrl = process.env.DATABASE_URL || 'postgres://postgres:postgres@localhost:5432/tenantpilot';
|
||||
console.log('🔌 Connecting to:', dbUrl.replace(/:[^:@]+@/, ':****@'));
|
||||
|
||||
const pool = new Pool({ connectionString: dbUrl });
|
||||
const db = drizzle(pool);
|
||||
|
||||
try {
|
||||
console.log('⏳ Removing duplicate rows...');
|
||||
await db.execute(sql`
|
||||
DELETE FROM policy_settings a
|
||||
USING policy_settings b
|
||||
WHERE a.id > b.id
|
||||
AND a.tenant_id = b.tenant_id
|
||||
AND a.graph_policy_id = b.graph_policy_id
|
||||
AND a.setting_name = b.setting_name
|
||||
`);
|
||||
|
||||
console.log('⏳ Dropping old index...');
|
||||
await db.execute(sql`DROP INDEX IF EXISTS policy_settings_upsert_idx`);
|
||||
|
||||
console.log('⏳ Adding unique constraint...');
|
||||
await db.execute(sql`
|
||||
ALTER TABLE policy_settings
|
||||
ADD CONSTRAINT policy_settings_upsert_unique
|
||||
UNIQUE(tenant_id, graph_policy_id, setting_name)
|
||||
`);
|
||||
|
||||
console.log('✅ Constraint added successfully!');
|
||||
await pool.end();
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
console.error('❌ Failed:', error);
|
||||
await pool.end();
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
fixConstraint();
|
||||
3
lib/db/migrations/0001_reflective_dormammu.sql
Normal file
3
lib/db/migrations/0001_reflective_dormammu.sql
Normal file
@ -0,0 +1,3 @@
|
||||
DROP INDEX "policy_settings_upsert_idx";--> statement-breakpoint
|
||||
ALTER TABLE "user" ADD COLUMN "tenant_id" text;--> statement-breakpoint
|
||||
ALTER TABLE "policy_settings" ADD CONSTRAINT "policy_settings_upsert_unique" UNIQUE("tenant_id","graph_policy_id","setting_name");
|
||||
450
lib/db/migrations/meta/0001_snapshot.json
Normal file
450
lib/db/migrations/meta/0001_snapshot.json
Normal file
@ -0,0 +1,450 @@
|
||||
{
|
||||
"id": "65f6746a-011c-4712-97db-c41f4b3e6547",
|
||||
"prevId": "7bea20d0-987b-4a12-8446-a5966f2eb3e8",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
"public.account": {
|
||||
"name": "account",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"userId": {
|
||||
"name": "userId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"type": {
|
||||
"name": "type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"provider": {
|
||||
"name": "provider",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"providerAccountId": {
|
||||
"name": "providerAccountId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"refresh_token": {
|
||||
"name": "refresh_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"access_token": {
|
||||
"name": "access_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"token_type": {
|
||||
"name": "token_type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"scope": {
|
||||
"name": "scope",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"id_token": {
|
||||
"name": "id_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"session_state": {
|
||||
"name": "session_state",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"account_userId_user_id_fk": {
|
||||
"name": "account_userId_user_id_fk",
|
||||
"tableFrom": "account",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"account_provider_providerAccountId_pk": {
|
||||
"name": "account_provider_providerAccountId_pk",
|
||||
"columns": [
|
||||
"provider",
|
||||
"providerAccountId"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.session": {
|
||||
"name": "session",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"sessionToken": {
|
||||
"name": "sessionToken",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"userId": {
|
||||
"name": "userId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"expires": {
|
||||
"name": "expires",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"session_userId_user_id_fk": {
|
||||
"name": "session_userId_user_id_fk",
|
||||
"tableFrom": "session",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.user": {
|
||||
"name": "user",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"emailVerified": {
|
||||
"name": "emailVerified",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"image": {
|
||||
"name": "image",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"tenant_id": {
|
||||
"name": "tenant_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.verificationToken": {
|
||||
"name": "verificationToken",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"identifier": {
|
||||
"name": "identifier",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"token": {
|
||||
"name": "token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"expires": {
|
||||
"name": "expires",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"verificationToken_identifier_token_pk": {
|
||||
"name": "verificationToken_identifier_token_pk",
|
||||
"columns": [
|
||||
"identifier",
|
||||
"token"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.policy_settings": {
|
||||
"name": "policy_settings",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"tenant_id": {
|
||||
"name": "tenant_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"policy_name": {
|
||||
"name": "policy_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"policy_type": {
|
||||
"name": "policy_type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"setting_name": {
|
||||
"name": "setting_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"setting_value": {
|
||||
"name": "setting_value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"graph_policy_id": {
|
||||
"name": "graph_policy_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"last_synced_at": {
|
||||
"name": "last_synced_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"policy_settings_tenant_id_idx": {
|
||||
"name": "policy_settings_tenant_id_idx",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "tenant_id",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
},
|
||||
"policy_settings_setting_name_idx": {
|
||||
"name": "policy_settings_setting_name_idx",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "setting_name",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"policy_settings_upsert_unique": {
|
||||
"name": "policy_settings_upsert_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"tenant_id",
|
||||
"graph_policy_id",
|
||||
"setting_name"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.subscriptions": {
|
||||
"name": "subscriptions",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"stripe_customer_id": {
|
||||
"name": "stripe_customer_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"stripe_subscription_id": {
|
||||
"name": "stripe_subscription_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"stripe_price_id": {
|
||||
"name": "stripe_price_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"stripe_current_period_end": {
|
||||
"name": "stripe_current_period_end",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"subscriptions_user_id_user_id_fk": {
|
||||
"name": "subscriptions_user_id_user_id_fk",
|
||||
"tableFrom": "subscriptions",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"subscriptions_user_id_stripe_customer_id_pk": {
|
||||
"name": "subscriptions_user_id_stripe_customer_id_pk",
|
||||
"columns": [
|
||||
"user_id",
|
||||
"stripe_customer_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {
|
||||
"subscriptions_user_id_unique": {
|
||||
"name": "subscriptions_user_id_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"user_id"
|
||||
]
|
||||
},
|
||||
"subscriptions_stripe_customer_id_unique": {
|
||||
"name": "subscriptions_stripe_customer_id_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"stripe_customer_id"
|
||||
]
|
||||
},
|
||||
"subscriptions_stripe_subscription_id_unique": {
|
||||
"name": "subscriptions_stripe_subscription_id_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"stripe_subscription_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"schemas": {},
|
||||
"sequences": {},
|
||||
"roles": {},
|
||||
"policies": {},
|
||||
"views": {},
|
||||
"_meta": {
|
||||
"columns": {},
|
||||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,13 @@
|
||||
"when": 1764967548076,
|
||||
"tag": "0000_tiny_skin",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "7",
|
||||
"when": 1765060303141,
|
||||
"tag": "0001_reflective_dormammu",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import { pgTable, text, timestamp, index } from 'drizzle-orm/pg-core';
|
||||
import { pgTable, text, timestamp, index, unique } from 'drizzle-orm/pg-core';
|
||||
import { createId } from '@paralleldrive/cuid2';
|
||||
|
||||
export const POLICY_TYPES = [
|
||||
@ -43,7 +43,8 @@ export const policySettings = pgTable(
|
||||
settingNameIdx: index('policy_settings_setting_name_idx').on(
|
||||
table.settingName
|
||||
),
|
||||
upsertIdx: index('policy_settings_upsert_idx').on(
|
||||
// Unique constraint for ON CONFLICT upsert
|
||||
upsertUnique: unique('policy_settings_upsert_unique').on(
|
||||
table.tenantId,
|
||||
table.graphPolicyId,
|
||||
table.settingName
|
||||
|
||||
Loading…
Reference in New Issue
Block a user